各位观众老爷,大家好!今天咱们来聊聊Python里的“工厂模式”,这玩意儿听起来好像很高大上,但其实核心思想特别简单,用人话说就是:“想要啥,跟工厂说一声,工厂给你造出来,你不用管它咋造的。”
咱们先从一个实际例子入手,看看为啥需要这玩意儿。
一、 场景:多种支付方式
假设我们要做一个电商网站,支付方式有很多种:支付宝、微信、银行卡、信用卡等等。每种支付方式都有自己的处理逻辑,比如支付宝需要跳转到支付宝页面,微信需要调起微信APP等等。
如果直接在订单处理的代码里写死这些逻辑,那代码就会变成这样:
def process_order(order, payment_method):
if payment_method == "alipay":
# 支付宝支付逻辑
print("跳转到支付宝页面...")
# ...
elif payment_method == "wechat":
# 微信支付逻辑
print("调起微信APP...")
# ...
elif payment_method == "bankcard":
# 银行卡支付逻辑
print("跳转到银行卡支付页面...")
# ...
else:
raise ValueError("不支持的支付方式")
# 使用
order = {"amount": 100}
process_order(order, "alipay")
process_order(order, "wechat")
这代码有什么问题?
- 臃肿不堪: 每次增加一种支付方式,就要修改
process_order
函数,这个函数会越来越长,越来越难维护。 - 耦合度高: 订单处理逻辑和支付方式的实现细节紧密耦合在一起,修改其中一个,很可能影响另一个。
- 扩展性差: 如果要增加新的支付方式,必须修改
process_order
函数,违反了“开闭原则”(对扩展开放,对修改关闭)。
这简直就是代码界的“屎山”,谁碰谁倒霉!
二、 解决方案:工厂模式登场
工厂模式就是来解决这种问题的。它的核心思想是:把对象的创建过程封装起来,让客户端(也就是调用方)不需要知道对象的具体类型,只需要告诉工厂“我要什么”,工厂就会返回相应的对象。
具体来说,我们可以这样做:
-
定义一个支付方式的抽象基类: 所有具体的支付方式都要继承这个基类,并实现统一的接口。
from abc import ABC, abstractmethod class PaymentGateway(ABC): @abstractmethod def pay(self, amount): pass
-
创建具体的支付方式类: 实现各种支付方式的具体逻辑。
class Alipay(PaymentGateway): def pay(self, amount): print(f"使用支付宝支付 {amount} 元") # 模拟支付过程 return True class WechatPay(PaymentGateway): def pay(self, amount): print(f"使用微信支付 {amount} 元") # 模拟支付过程 return True class BankCardPay(PaymentGateway): def pay(self, amount): print(f"使用银行卡支付 {amount} 元") # 模拟支付过程 return True
-
创建一个工厂类: 根据客户端的请求,创建相应的支付方式对象。
class PaymentGatewayFactory: def create_payment_gateway(self, payment_method): if payment_method == "alipay": return Alipay() elif payment_method == "wechat": return WechatPay() elif payment_method == "bankcard": return BankCardPay() else: raise ValueError("不支持的支付方式")
-
修改订单处理函数: 使用工厂来创建支付方式对象,并调用其支付方法。
def process_order(order, payment_method): factory = PaymentGatewayFactory() payment_gateway = factory.create_payment_gateway(payment_method) if payment_gateway.pay(order["amount"]): print("支付成功!") else: print("支付失败!") # 使用 order = {"amount": 100} process_order(order, "alipay") process_order(order, "wechat")
现在,process_order
函数的代码就变得清晰多了,它只需要知道支付方式的名称,而不需要关心具体的实现细节。如果要增加新的支付方式,只需要添加一个新的支付方式类,并在工厂类中注册即可,不需要修改 process_order
函数。
三、 工厂模式的优点
- 解耦: 将对象的创建和使用分离,降低了代码的耦合度。
- 可扩展性: 增加新的对象类型,只需要修改工厂类,而不需要修改客户端代码。
- 灵活性: 可以根据不同的参数,创建不同的对象,提供了更大的灵活性。
- 隐藏实现细节: 客户端不需要知道对象的具体类型和创建过程,只需要知道接口即可。
四、 工厂模式的分类
工厂模式主要有三种类型:
- 简单工厂模式(Simple Factory Pattern): 一个工厂类负责创建所有类型的对象。 (就是上面我们举的例子)
- 工厂方法模式(Factory Method Pattern): 定义一个创建对象的接口,让子类决定实例化哪个类。
- 抽象工厂模式(Abstract Factory Pattern): 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
咱们一个个来分析一下:
1. 简单工厂模式
简单工厂模式,也叫静态工厂模式。 顾名思义,它最简单,也最常用。
优点:
- 简单易懂,易于实现。
缺点:
- 工厂类职责过重,违反了单一职责原则。
- 当需要增加新的产品类型时,需要修改工厂类,违反了开闭原则。
适用场景:
- 创建对象的逻辑比较简单,对象类型不多。
- 客户端只需要知道传入工厂类的参数,就可以创建对象。
2. 工厂方法模式
工厂方法模式,定义一个创建对象的接口,但将实际创建对象的过程延迟到子类中。每个产品都有一个对应的工厂。
优点:
- 符合开闭原则,增加新的产品类型,只需要增加一个对应的工厂类即可。
- 每个工厂类只负责创建一种产品,符合单一职责原则。
缺点:
- 类的数量会增加,增加了系统的复杂性。
适用场景:
- 客户端不知道它所需要的对象的类。
- 一个类通过其子类来指定创建哪个对象。
举个例子,还是支付方式,用工厂方法模式实现:
from abc import ABC, abstractmethod
# 抽象产品接口
class PaymentGateway(ABC):
@abstractmethod
def pay(self, amount):
pass
# 具体产品
class Alipay(PaymentGateway):
def pay(self, amount):
print(f"使用支付宝支付 {amount} 元")
return True
class WechatPay(PaymentGateway):
def pay(self, amount):
print(f"使用微信支付 {amount} 元")
return True
# 抽象工厂接口
class PaymentGatewayFactory(ABC):
@abstractmethod
def create_payment_gateway(self):
pass
# 具体工厂
class AlipayFactory(PaymentGatewayFactory):
def create_payment_gateway(self):
return Alipay()
class WechatPayFactory(PaymentGatewayFactory):
def create_payment_gateway(self):
return WechatPay()
# 客户端代码
def process_order(order, payment_method):
if payment_method == "alipay":
factory = AlipayFactory()
elif payment_method == "wechat":
factory = WechatPayFactory()
else:
raise ValueError("不支持的支付方式")
payment_gateway = factory.create_payment_gateway()
if payment_gateway.pay(order["amount"]):
print("支付成功!")
else:
print("支付失败!")
# 使用
order = {"amount": 100}
process_order(order, "alipay")
process_order(order, "wechat")
3. 抽象工厂模式
抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 它解决的是“产品族”的问题,比如一个操作系统,有不同的UI风格(Windows风格、Mac风格),每种UI风格又有很多组件(按钮、文本框、窗口等等)。
优点:
- 可以保证客户端使用的产品来自同一个产品族。
- 易于替换整个产品族。
缺点:
- 难以支持新的产品族,因为需要在抽象工厂接口中增加新的方法,会导致所有子类都需要修改。
- 增加了系统的复杂性。
适用场景:
- 需要创建一系列相关或相互依赖的对象。
- 需要切换不同的产品族。
举个例子,假设我们要创建不同风格的GUI组件(按钮和文本框):
from abc import ABC, abstractmethod
# 抽象产品:按钮
class Button(ABC):
@abstractmethod
def render(self):
pass
# 抽象产品:文本框
class TextBox(ABC):
@abstractmethod
def render(self):
pass
# 具体产品:Windows按钮
class WindowsButton(Button):
def render(self):
print("渲染Windows风格按钮")
# 具体产品:Windows文本框
class WindowsTextBox(TextBox):
def render(self):
print("渲染Windows风格文本框")
# 具体产品:Mac按钮
class MacButton(Button):
def render(self):
print("渲染Mac风格按钮")
# 具体产品:Mac文本框
class MacTextBox(TextBox):
def render(self):
print("渲染Mac风格文本框")
# 抽象工厂
class GUIFactory(ABC):
@abstractmethod
def create_button(self):
pass
@abstractmethod
def create_text_box(self):
pass
# 具体工厂:Windows工厂
class WindowsFactory(GUIFactory):
def create_button(self):
return WindowsButton()
def create_text_box(self):
return WindowsTextBox()
# 具体工厂:Mac工厂
class MacFactory(GUIFactory):
def create_button(self):
return MacButton()
def create_text_box(self):
return MacTextBox()
# 客户端代码
def create_gui(factory):
button = factory.create_button()
text_box = factory.create_text_box()
button.render()
text_box.render()
# 使用
windows_factory = WindowsFactory()
create_gui(windows_factory)
mac_factory = MacFactory()
create_gui(mac_factory)
五、 何时使用工厂模式
说了这么多,那到底啥时候该用工厂模式呢?
- 当对象的创建逻辑比较复杂时。 比如需要根据不同的参数创建不同的对象,或者需要进行复杂的初始化操作。
- 当需要解耦对象的创建和使用时。 让客户端不需要知道对象的具体类型和创建过程。
- 当需要提供一个统一的接口来创建对象时。 比如需要切换不同的实现,或者需要提供不同的产品族。
- 当对象需要被延迟创建时。 比如只有在真正需要使用对象的时候才创建。
用一张表格总结一下:
模式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
简单工厂 | 简单易懂,易于实现。 | 工厂类职责过重,违反了单一职责原则;增加新的产品类型,需要修改工厂类,违反了开闭原则。 | 创建对象的逻辑比较简单,对象类型不多;客户端只需要知道传入工厂类的参数,就可以创建对象。 |
工厂方法 | 符合开闭原则,增加新的产品类型,只需要增加一个对应的工厂类即可;每个工厂类只负责创建一种产品,符合单一职责原则。 | 类的数量会增加,增加了系统的复杂性。 | 客户端不知道它所需要的对象的类;一个类通过其子类来指定创建哪个对象。 |
抽象工厂 | 可以保证客户端使用的产品来自同一个产品族;易于替换整个产品族。 | 难以支持新的产品族,因为需要在抽象工厂接口中增加新的方法,会导致所有子类都需要修改;增加了系统的复杂性。 | 需要创建一系列相关或相互依赖的对象;需要切换不同的产品族。 |
六、 工厂模式的注意事项
- 不要过度使用工厂模式。 如果对象的创建逻辑很简单,直接使用构造函数即可,没有必要引入工厂模式。
- 选择合适的工厂模式。 根据实际情况选择合适的工厂模式,不要盲目追求高级模式。
- 注意代码的可读性和可维护性。 工厂模式虽然可以提高代码的灵活性,但也可能增加代码的复杂性,需要注意代码的可读性和可维护性。
七、 Python中的工厂模式实现技巧
- 使用
__new__
方法: 可以在__new__
方法中创建对象,实现更灵活的工厂模式。 - 使用元类(Metaclass): 可以使用元类来自动注册工厂类,减少代码的冗余。
- 使用装饰器(Decorator): 可以使用装饰器来简化工厂类的创建过程。
八、 总结
工厂模式是一种常用的设计模式,可以帮助我们更好地组织代码,提高代码的灵活性和可维护性。 希望通过今天的讲解,大家能够对工厂模式有一个更深入的了解,并在实际项目中灵活运用。
记住,“想要啥,跟工厂说一声,工厂给你造出来,你不用管它咋造的。” 这就是工厂模式的精髓!
感谢各位观众老爷的观看,下次再见!