当前位置: 首页 > news >正文

基于odoo17的设计模式详解---工厂模式

大家好,我是你的Odoo技术伙伴。在Odoo开发中,我们几乎每天都在创建各种对象:新的客户记录、销售订单、发票、库存移动等等。虽然表面上我们只是简单地调用self.env['some.model'].create(...),但在这看似简单的操作背后,Odoo的ORM扮演着一个极其强大和复杂的工厂(Factory) 角色。今天,我们将深入探讨经典的工厂模式(Factory Pattern),并揭示Odoo是如何通过其核心架构,将工厂模式的思想运用到极致,从而实现了对象创建的解耦、灵活性和可扩展性。

一、什么是工厂模式?

让我们从一个简单的比喻开始:去一家车行买车。

你走进一家大型汽车品牌的展厅,告诉销售顾问:“我想要一辆红色的、带天窗的SUV。”

  • 你(客户端 Client):提出了一个需求(创建一辆特定的车)。
  • 销售顾问和其背后的生产系统(工厂 Factory):接收你的需求。
  • 汽车(产品 Product):最终生产出的那辆SUV。

你不需要关心这辆SUV是在哪个城市的哪个工厂生产的,它的底盘、发动机、车漆是如何一步步组装起来的。你只需要向“工厂”(车行)下达指令,它就会负责所有复杂的创建细节,并最终交付给你一个符合要求的产品。如果这家车行还卖轿车和卡车,你同样可以用类似的方式下单,工厂会根据你的需求,调用不同的生产线来制造。

转换成软件设计的语言:

工厂模式是一种创建型设计模式,它提供一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。其核心思想是:将对象的实例化过程封装在一个专门的“工厂”中,由工厂来决定具体创建哪个类的实例。

二、Odoo ORM:终极的对象创建工厂

在Odoo中,最核心、最强大的工厂就是Odoo的环境和ORM本身。当你写下self.env['res.partner']时,你得到的并不是一个ResPartner类,而是一个与该模型关联的、功能强大的模型代理对象。这个代理对象,就是我们与ORM工厂交互的入口。

1. 简单工厂模式的体现:self.env[model_name]

self.env这个“超级工厂”可以根据你提供的“产品型号”(模型名称字符串),为你生产出对应的“产品操作器”(模型代理对象)。

# 向工厂请求 'res.partner' 产品的操作器
partner_factory_proxy = self.env['res.partner']# 向工厂请求 'sale.order' 产品的操作器
order_factory_proxy = self.env['sale.order']# 使用操作器来创建具体的产品实例 (记录)
new_partner = partner_factory_proxy.create({'name': 'Factory-made Partner'})

这里,self.env扮演了简单工厂的角色。客户端(你的代码)通过一个字符串参数,来决定需要哪个“生产线”,而无需直接导入和实例化ResPartnerSaleOrder这些具体的模型类。这实现了客户端与具体模型类的解耦。

2. 工厂方法模式的体现:可被子类重写的创建逻辑

工厂方法模式的核心是:定义一个用于创建对象的接口,但让子类决定实例化哪个类。在Odoo中,这个思想通过重写create()方法来完美实现。

create()方法就是那个“创建对象的接口”。每个模型都可以通过继承来重写它,从而改变或增强其创建过程。

场景:创建一个销售订单时,自动生成一个唯一的、带前缀的序列号。Odoo的sale.order模型就是这么做的。

# addons/sale/models/sale_order.py
from odoo import models, fields, apiclass SaleOrder(models.Model):_inherit = 'sale.order'name = fields.Char(..., copy=False, default='/')@api.model_create_multidef create(self, vals_list):# --- 这是被重写的工厂方法 ---for vals in vals_list:# 1. 在创建前,执行特殊的逻辑if 'name' not in vals or vals.get('name') == '/':# 向序列号子系统(另一个工厂)请求一个新序列号vals['name'] = self.env['ir.sequence'].next_by_code('sale.order') or '/'# 2. 调用父类的工厂方法,完成基础的对象创建orders = super(SaleOrder, self).create(vals_list)# 3. 在创建后,执行其他逻辑...return orders

代码解读:

  • create()是工厂方法:它负责创建sale.order的实例。
  • 子类决定实现SaleOrder类(作为models.Model的子类)重写了create方法,加入了自己独特的实例化逻辑(生成序列号)。
  • 客户端无感:调用self.env['sale.order'].create(...)的代码,完全不知道背后还有生成序列号这么复杂的过程。它只是向工厂下达了指令,工厂则按照SaleOrder这条“特殊生产线”的规定,完成了产品的创建。
  • 扩展性:任何一个模块都可以通过_inherit再次重写create方法,进一步定制这条生产线,而不会影响到客户端代码。

3. 抽象工厂模式的影子:提供一系列相关的对象

抽象工厂模式旨在提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。在Odoo中,一些复杂的业务方法也扮演了抽象工厂的角色。

场景:确认销售订单时,需要同时创建提货单和库存移动。

sale.orderaction_confirm()方法(一个外观方法)内部,调用了_create_stock_moves()等方法。这个过程就像一个“物流单据抽象工厂”。

  • 抽象工厂接口:“为销售订单准备物流”这个业务概念。
  • 具体工厂action_confirm的实现。
  • 抽象产品:“物流单据”。
  • 具体产品stock.picking(提货单)实例和stock.move(库存移动)实例。
# 伪代码,简化自 sale.order
def action_confirm(self):# ...# 调用一个“工厂方法”来创建提货单pickings = self._create_pickings() # 另一个“工厂方法”来创建库存移动,并关联到提货单moves = self.order_line._create_stock_moves(pickings)# ...

客户端只需要调用order.action_confirm(),这个“抽象工厂”就会负责创建出一整套相互关联的物流对象(提货单和其下的库存移动),而客户端无需关心stock.pickingstock.move的创建细节和它们之间的关联逻辑。

四、优势与适用场景

优势

  • 高度解耦:将对象的创建逻辑与使用逻辑分离。客户端只依赖于工厂和产品接口,不依赖于具体的产品实现。
  • 单一职责:创建对象的复杂逻辑被集中到工厂中,使得代码结构更清晰。
  • 易于扩展:增加一个新的产品类型,只需要创建一个新的产品类,并在工厂中增加一个相应的创建分支即可,无需修改客户端代码,符合开闭原则。在Odoo中,就是通过创建一个新模块来继承和重写create方法。
  • 集中控制:可以在工厂方法中加入权限检查、日志记录、资源池管理等统一的控制逻辑。

结论

工厂模式是Odoo框架设计的基石。它并非以某个僵硬的Factory基类形式存在,而是一种内化于ORM核心、贯穿于模块化开发中的设计哲学。

  • self.env 是一个强大的简单工厂,负责按需提供模型操作的入口。
  • 可被重写的create()方法 是工厂方法模式的完美体现,它赋予了每个模型定制自身创建过程的能力。
  • 复杂的业务方法 则常常扮演抽象工厂的角色,负责创建一系列相互关联的对象。

理解Odoo中的工厂模式,意味着你能够洞察Odoo是如何在保证灵活性的同时,管理着成百上千个模型的实例化过程。它将帮助你编写出更符合Odoo架构、更具扩展性和维护性的代码,让你从一个简单的“对象使用者”,成长为一个懂得如何“设计对象生产线”的架构师。

http://www.dtcms.com/a/269561.html

相关文章:

  • 阿里云mysql数据丢失,如何通过服务器备份在其他服务器上恢复数据,并获取mysql丢失数据,完成mysql数据恢复
  • Prompt Injection Attack to Tool Selection in LLM Agents
  • 深度剖析:向70岁老系统植入通信芯片——MCP注入构建未来级分布式通信
  • IP 能ping通,服务器是否开机?
  • Go语言反射机制详解
  • 基于ZYNQ7000的AD9226采集卡实现(3、PS LINUX DMA驱动实现)
  • vue3 el-table 行数据沾满格自动换行
  • 【debug】git clone 报错
  • Web前端: :is(通用选择器)
  • 图像轮廓检测与绘制:OpenCV 实战指南
  • claude code-- 基于Claude 4 模型的智能编程工具,重塑你的编程体验
  • 微软上线Deep Research:OpenAI同款智能体,o3+必应双王炸
  • Web后端开发-Mybatis
  • 玩转Docker | 使用Docker部署NotepadMX笔记应用程序
  • UDP的socket编程
  • unity 模型UV重叠问题相关(重新整理)
  • BUUCTF在线评测-练习场-WebCTF习题[GXYCTF2019]BabySQli1-flag获取、解析
  • 无法访问宝塔面板 - 特网科技
  • Coze智能体平台全景解析:从零构建企业级AI应用的实战指南
  • Spring Boot 企业项目技术选型
  • UI前端大数据可视化实战策略:如何设计符合用户认知的数据展示方式?
  • 京东携手HarmonyOS SDK首发家电AR高精摆放功能
  • 开发在线商店:基于Vue2+ElementUI的电商平台前端实践
  • 二刷(李宏毅深度学习,醍醐灌顶,长刷长爽)
  • AI技术通过提示词工程(Prompt Engineering)正在深度重塑职场生态和行业格局,这种变革不仅体现在效率提升,更在重构人机协作模式。
  • 车载网络安全是当代车辆功能很重要的组成部分
  • 语言模型 RLHF 实践指南(一):策略网络、价值网络与 PPO 损失函数
  • 【OceanBase 诊断调优】—— SQL 查询触发笛卡尔积怎么处理
  • Rust BSS段原理与实践解析
  • 自动驾驶感知系统