02、Python - 普通工厂模式:从if-else地狱到企业级解耦的完整演进指南

文章目录
- [02、Python - 普通工厂模式:从if-else地狱到企业级解耦的完整演进指南](#02、Python - 普通工厂模式:从if-else地狱到企业级解耦的完整演进指南)
-
- 一、痛点场景:你是不是也在写这样的代码?
- 二、什么是工厂模式
-
- [2.1 专业解释](#2.1 专业解释)
- [2.2 大白话理解](#2.2 大白话理解)
- [2.3 生活案例](#2.3 生活案例)
- 三、为什么要用工厂模式
-
- [3.1 解耦:客户端不依赖具体类](#3.1 解耦:客户端不依赖具体类)
- [3.2 符合开闭原则](#3.2 符合开闭原则)
- [3.3 隐藏创建细节](#3.3 隐藏创建细节)
- [3.4 统一管理与控制](#3.4 统一管理与控制)
- 四、工厂模式是怎么演进过来的
-
- [4.1 第一阶段:直接new对象](#4.1 第一阶段:直接new对象)
- [4.2 第二阶段:简单工厂模式](#4.2 第二阶段:简单工厂模式)
- [4.3 第三阶段:工厂方法模式](#4.3 第三阶段:工厂方法模式)
- 五、简单工厂模式详解
-
- [5.1 定义与结构](#5.1 定义与结构)
- [5.2 代码实现](#5.2 代码实现)
- [5.3 进阶写法:用字典替代if-else](#5.3 进阶写法:用字典替代if-else)
- [5.4 优缺点分析](#5.4 优缺点分析)
- 六、工厂方法模式详解
-
- [6.1 定义与结构](#6.1 定义与结构)
- [6.2 代码实现](#6.2 代码实现)
- [6.3 优缺点分析](#6.3 优缺点分析)
- 七、企业项目实战:多支付网关工厂
-
- [7.1 业务背景](#7.1 业务背景)
- [7.2 代码实现](#7.2 代码实现)
- [7.3 这个设计好在哪里](#7.3 这个设计好在哪里)
- 八、常用场景总结
- 九、面试官高频面试题
- 十、总结
一、痛点场景:你是不是也在写这样的代码?

在开始讲工厂模式之前,先来看一段几乎每个Python开发者都写过的代码。
假设你正在开发一个电商支付系统,需要支持多种支付方式:支付宝、微信支付、银联支付。最直观的写法大概是这样的:
python
class Alipay:
def pay(self, amount):
print(f"支付宝支付 {amount} 元")
class WeChatPay:
def pay(self, amount):
print(f"微信支付 {amount} 元")
class UnionPay:
def pay(self, amount):
print(f"银联支付 {amount} 元")
def process_payment(payment_type, amount):
if payment_type == "alipay":
gateway = Alipay()
elif payment_type == "wechat":
gateway = WeChatPay()
elif payment_type == "unionpay":
gateway = UnionPay()
else:
raise ValueError(f"不支持的支付方式: {payment_type}")
gateway.pay(amount)
这段代码能跑,但问题很大。
问题一:违反开闭原则。 产品经理说要加一个Stripe支付,你就得回去改process_payment函数,加一个elif分支。每加一种支付方式,就要动一次核心业务代码。改一次,测一次,回归一次,累不累?
问题二:违反单一职责原则。 process_payment函数既负责判断支付类型,又负责创建对象,还负责调用支付。一个函数干了三件事,出了bug你都不知道是哪一层的问题。
问题三:代码膨胀。 当支付方式从3种变成10种,这个函数就会变成一个长达几十行的if-else怪物。新来的同事看了直摇头,代码评审时被怼得哑口无言。
问题四:创建逻辑分散。 如果Alipay的初始化需要传入app_id、app_secret、gateway_url等一堆参数,这些参数散落在业务代码的各个角落,改一个配置要全局搜索替换。
这就是典型的"if-else地狱"。而工厂模式,就是来解救你的。
二、什么是工厂模式
2.1 专业解释
工厂模式(Factory Pattern)是一种创建型设计模式,其核心思想是将对象的创建逻辑与使用逻辑分离,通过专门的工厂类或工厂方法来负责对象的实例化,从而实现调用方与具体产品类之间的解耦。
普通工厂模式主要包含两种形态:
- 简单工厂模式(Simple Factory):由一个统一的工厂类,根据传入的参数决定创建哪一种产品实例。它不属于GoF 23种经典设计模式,但在工程实践中极为常用。
- 工厂方法模式(Factory Method):定义一个创建对象的抽象工厂接口,让子类决定实例化哪一个类,每个具体产品对应一个具体工厂。它是GoF 23种经典设计模式之一。
2.2 大白话理解
说人话就是:你不要自己new对象了,你告诉工厂你要什么,工厂给你造好送过来。
就像你去餐厅吃饭,你不需要自己进厨房炒菜,你只需要跟服务员说"来一份宫保鸡丁",后厨(工厂)就会把菜做好端给你。你不需要知道这道菜用了什么油、什么火候、放了多少花椒,你只负责吃。
2.3 生活案例
案例一:手机城 vs 品牌专卖店
你想买手机,有两种选择:
- 去一个综合手机城(简单工厂),里面什么品牌都有卖,你跟店员说"我要苹果",他就给你拿苹果;说"我要华为",他就给你拿华为。一个店搞定所有品牌。
- 去品牌专卖店(工厂方法),苹果只在苹果专卖店卖,华为只在华为专卖店卖。你想买苹果就去苹果店,想买华为就去华为店。每家店只卖自己的品牌。
这就是简单工厂和工厂方法最直观的区别。
案例二:奶茶店点单
你去奶茶店,告诉店员"我要一杯珍珠奶茶,三分糖,去冰"。店员不会让你自己去后厨调,而是把订单传给后厨,后厨做好了递出来。你(客户端)只需要描述需求,不需要关心制作过程。这就是工厂模式的日常体现。
三、为什么要用工厂模式
3.1 解耦:客户端不依赖具体类
用了工厂模式之后,客户端代码只依赖抽象产品接口,不依赖具体的产品类。具体类的名字不会散落在业务代码的各个角落,将来换实现、换品牌,只需要改工厂一处地方。
3.2 符合开闭原则
工厂方法模式下,新增产品时只需要新增一个产品类和对应的工厂类,不需要修改已有代码。对扩展开放,对修改关闭,这就是开闭原则的核心要义。
3.3 隐藏创建细节
对象的创建可能很复杂,需要加载配置、初始化连接、校验参数、注册回调等等。这些逻辑封装在工厂里,客户端完全无感,调用方代码干净清爽。
3.4 统一管理与控制
工厂是对象创建的唯一入口,可以在创建过程中统一加入日志记录、权限校验、对象池管理、单例控制等横切逻辑。比如你想统计每天创建了多少个支付网关实例,在工厂里加一行日志就够了。
四、工厂模式是怎么演进过来的

工厂模式不是凭空出现的,它是程序员在实际开发中一步步踩坑踩出来的。整个演进过程分为三个阶段。
4.1 第一阶段:直接new对象
最原始的写法,客户端哪里需要对象就在哪里new。
python
def order_pizza(pizza_type):
if pizza_type == "cheese":
pizza = CheesePizza()
elif pizza_type == "pepperoni":
pizza = PepperoniPizza()
elif pizza_type == "veggie":
pizza = VeggiePizza()
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
痛点 :创建逻辑和业务逻辑混在一起,每加一种披萨就要改order_pizza函数。如果有10个地方都在创建披萨对象,那就要改10处。
4.2 第二阶段:简单工厂模式
把对象创建的逻辑抽出来,放到一个专门的工厂类里。客户端只需要调用工厂的方法,传入参数,工厂返回对应的对象。
python
class SimplePizzaFactory:
def create_pizza(self, pizza_type):
if pizza_type == "cheese":
return CheesePizza()
elif pizza_type == "pepperoni":
return PepperoniPizza()
elif pizza_type == "veggie":
return VeggiePizza()
return None
class PizzaStore:
def __init__(self, factory):
self.factory = factory
def order_pizza(self, pizza_type):
pizza = self.factory.create_pizza(pizza_type)
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
进步 :创建逻辑集中到了工厂类,客户端PizzaStore不再关心具体怎么创建披萨。加新披萨时只需要改工厂类,不用动业务代码。
遗留问题:加新产品时还是要改工厂类的if-else,仍然违反开闭原则。工厂类承担了所有产品的创建职责,违反单一职责原则。
4.3 第三阶段:工厂方法模式
把工厂也抽象成接口,每个具体产品对应一个具体工厂。新增产品时,新增一个工厂类即可,完全不用改已有代码。
python
from abc import ABC, abstractmethod
class PizzaFactory(ABC):
@abstractmethod
def create_pizza(self):
pass
class CheesePizzaFactory(PizzaFactory):
def create_pizza(self):
return CheesePizza()
class PepperoniPizzaFactory(PizzaFactory):
def create_pizza(self):
return PepperoniPizza()
class VeggiePizzaFactory(PizzaFactory):
def create_pizza(self):
return VeggiePizza()
进步:完全符合开闭原则,新增产品零修改。每个工厂只负责一种产品,符合单一职责原则。
代价:类的数量翻倍,每加一种产品就要加两个类(产品类+工厂类),代码结构变复杂。
这就是工厂模式的完整演进路线:从直接new,到简单工厂,再到工厂方法。每一步都在解决上一步的痛点,但也引入了新的复杂度。没有银弹,只有权衡。
五、简单工厂模式详解
5.1 定义与结构
简单工厂模式(Simple Factory Pattern),又称为静态工厂模式。它由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类的实例。
核心角色:
- 抽象产品(Product):定义产品的公共接口,可以是抽象类或普通基类。
- 具体产品(Concrete Product):实现抽象产品接口的具体类,是工厂创建的目标。
- 工厂(Factory):负责创建所有具体产品实例的类,内部包含创建逻辑。

5.2 代码实现
以手机购买为例,用Python实现简单工厂模式:
python
# ==================== 产品层 ====================
class BasePhone:
"""手机抽象基类"""
def __init__(self, model, color):
self.model = model
self.color = color
def get_info(self):
return (f"A {self.color} mobile phone, "
f"the brand is {self.brand}, "
f"the model is {self.model}")
class Samsung(BasePhone):
def __init__(self, model, color):
self.brand = self.__class__.__name__
super().__init__(model, color)
class Apple(BasePhone):
def __init__(self, model, color):
self.brand = self.__class__.__name__
super().__init__(model, color)
class Huawei(BasePhone):
def __init__(self, model, color):
self.brand = self.__class__.__name__
super().__init__(model, color)
# ==================== 工厂层 ====================
class MobileCity:
"""手机城:简单工厂,一个店卖所有品牌"""
def shop_phone(self, brand, model, color):
brand_dict = {
"Samsung": Samsung,
"Apple": Apple,
"Huawei": Huawei,
}
cls = brand_dict.get(brand)
if cls:
return cls(model, color)
raise TypeError(f"no brand: {brand}")
# ==================== 客户端 ====================
if __name__ == "__main__":
store = MobileCity()
iphone_x = store.shop_phone(brand="Apple", model="X", color="black")
samsung_note7 = store.shop_phone(brand="Samsung", model="Note 7", color="blue")
huawei_p10 = store.shop_phone(brand="Huawei", model="P10", color="white")
print(iphone_x.get_info())
print(samsung_note7.get_info())
print(huawei_p10.get_info())
运行输出:
text
A black mobile phone, the brand is Apple, the model is X
A blue mobile phone, the brand is Samsung, the model is Note 7
A white mobile phone, the brand is Huawei, the model is P10
5.3 进阶写法:用字典替代if-else
上面的代码用了字典映射,比传统的if-else更优雅。但每次新增品牌还是要改brand_dict。有没有更好的方式?有,用注册机制:
python
class MobileCity:
"""支持动态注册的简单工厂"""
_brand_registry = {}
@classmethod
def register(cls, brand_name, brand_cls):
cls._brand_registry[brand_name] = brand_cls
def shop_phone(self, brand, model, color):
cls = self._brand_registry.get(brand)
if cls:
return cls(model, color)
raise TypeError(f"no brand: {brand}")
# 注册品牌
MobileCity.register("Samsung", Samsung)
MobileCity.register("Apple", Apple)
MobileCity.register("Huawei", Huawei)
这样新增品牌时,只需要调用MobileCity.register("Xiaomi", Xiaomi),不需要修改工厂类内部代码。这是简单工厂模式在Python中的一种优化实践。
5.4 优缺点分析
优点:
- 隐藏对象创建的细节,客户端无需知道对象如何构建
- 客户端与具体产品类解耦,不需要直接引用具体类名
- 结构简单,易于理解和实现
缺点:
- 违反单一职责原则,将所有产品的创建逻辑集中在一个工厂类里
- 新增产品时需要修改工厂类代码,违反开闭原则
- 产品类型过多时,工厂类会变得臃肿庞大
适用场景:产品类型较少且相对稳定,创建逻辑不复杂的场景。比如配置解析器、日志级别处理器等。
六、工厂方法模式详解
6.1 定义与结构
工厂方法模式(Factory Method Pattern)定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
核心角色:
- 抽象产品(Product):定义产品的公共接口。
- 具体产品(Concrete Product):实现抽象产品接口的具体类。
- 抽象工厂(Creator):声明工厂方法,返回一个产品对象。
- 具体工厂(Concrete Creator):实现工厂方法,创建具体产品实例。

6.2 代码实现
同样以手机购买为例,用Python实现工厂方法模式:
python
from abc import ABC, abstractmethod
# ==================== 产品层 ====================
class BasePhone:
"""手机抽象基类"""
def __init__(self, model, color):
self.model = model
self.color = color
def get_info(self):
return (f"A {self.color} mobile phone, "
f"the brand is {self.brand}, "
f"the model is {self.model}")
class Samsung(BasePhone):
def __init__(self, model, color):
self.brand = self.__class__.__name__
super().__init__(model, color)
class Apple(BasePhone):
def __init__(self, model, color):
self.brand = self.__class__.__name__
super().__init__(model, color)
class Huawei(BasePhone):
def __init__(self, model, color):
self.brand = self.__class__.__name__
super().__init__(model, color)
# ==================== 工厂层 ====================
class PhoneStore(ABC):
"""抽象工厂:手机专卖店接口"""
@abstractmethod
def shop_phone(self, model, color):
pass
class SamsungStore(PhoneStore):
def shop_phone(self, model, color):
return Samsung(model, color)
class AppleStore(PhoneStore):
def shop_phone(self, model, color):
return Apple(model, color)
class HuaweiStore(PhoneStore):
def shop_phone(self, model, color):
return Huawei(model, color)
# ==================== 客户端 ====================
if __name__ == "__main__":
samsung_store = SamsungStore()
apple_store = AppleStore()
huawei_store = HuaweiStore()
iphone_x = apple_store.shop_phone(model="X", color="black")
samsung_note7 = samsung_store.shop_phone(model="Note 7", color="blue")
huawei_p10 = huawei_store.shop_phone(model="P10", color="white")
print(iphone_x.get_info())
print(samsung_note7.get_info())
print(huawei_p10.get_info())
运行输出与简单工厂完全一致,但内部结构已经发生了本质变化。每个品牌有自己的专卖店,新增品牌时只需要新增一个产品类和一个工厂类,完全不用修改已有代码。
6.3 优缺点分析
优点:
- 每个具体产品对应一个具体工厂,新增产品时不需要修改已有工厂代码,符合开闭原则
- 隐藏了对象创建的细节,客户端只依赖抽象工厂和抽象产品
- 符合单一职责原则,每个工厂只负责创建一种产品
缺点:
- 每增加一个具体产品类,就必须增加一个相应的具体工厂类,类的数量成倍增加
- 增加了系统的抽象性和理解难度,对于简单场景可能过度设计
适用场景:产品类型较多且需要频繁扩展,客户端不依赖具体产品类的场景。比如插件系统、支付网关、消息队列连接器等。
七、企业项目实战:多支付网关工厂

理论讲完了,来看一个真正能在企业项目中落地的实战案例。
7.1 业务背景
某电商平台需要支持多种支付方式:支付宝、微信支付、Stripe(海外)、PayPal(海外)。每种支付方式的SDK调用方式、参数格式、回调处理都不一样。业务方希望调用方只需要传一个支付类型和金额,就能完成支付,不需要关心底层差异。
7.2 代码实现
python
from abc import ABC, abstractmethod
from typing import Dict, Any
# ==================== 抽象产品 ====================
class PaymentGateway(ABC):
"""支付网关抽象接口"""
@abstractmethod
def pay(self, amount: float, order_id: str) -> Dict[str, Any]:
"""发起支付,返回支付结果"""
pass
@abstractmethod
def refund(self, payment_id: str, amount: float) -> Dict[str, Any]:
"""退款"""
pass
@abstractmethod
def query(self, payment_id: str) -> Dict[str, Any]:
"""查询支付状态"""
pass
# ==================== 具体产品 ====================
class AlipayGateway(PaymentGateway):
def __init__(self, app_id: str, private_key: str, gateway_url: str):
self.app_id = app_id
self.private_key = private_key
self.gateway_url = gateway_url
def pay(self, amount: float, order_id: str) -> Dict[str, Any]:
# 实际项目中调用支付宝SDK
print(f"[支付宝] 订单 {order_id} 支付 {amount} 元")
return {"status": "success", "payment_id": f"alipay_{order_id}"}
def refund(self, payment_id: str, amount: float) -> Dict[str, Any]:
print(f"[支付宝] 退款 {payment_id} 金额 {amount} 元")
return {"status": "success", "refund_id": f"refund_{payment_id}"}
def query(self, payment_id: str) -> Dict[str, Any]:
return {"status": "paid", "payment_id": payment_id}
class WeChatPayGateway(PaymentGateway):
def __init__(self, mch_id: str, api_key: str, cert_path: str):
self.mch_id = mch_id
self.api_key = api_key
self.cert_path = cert_path
def pay(self, amount: float, order_id: str) -> Dict[str, Any]:
print(f"[微信支付] 订单 {order_id} 支付 {amount} 元")
return {"status": "success", "payment_id": f"wechat_{order_id}"}
def refund(self, payment_id: str, amount: float) -> Dict[str, Any]:
print(f"[微信支付] 退款 {payment_id} 金额 {amount} 元")
return {"status": "success", "refund_id": f"refund_{payment_id}"}
def query(self, payment_id: str) -> Dict[str, Any]:
return {"status": "paid", "payment_id": payment_id}
class StripeGateway(PaymentGateway):
def __init__(self, api_key: str, webhook_secret: str):
self.api_key = api_key
self.webhook_secret = webhook_secret
def pay(self, amount: float, order_id: str) -> Dict[str, Any]:
print(f"[Stripe] 订单 {order_id} 支付 ${amount}")
return {"status": "success", "payment_id": f"stripe_{order_id}"}
def refund(self, payment_id: str, amount: float) -> Dict[str, Any]:
print(f"[Stripe] 退款 {payment_id} 金额 ${amount}")
return {"status": "success", "refund_id": f"refund_{payment_id}"}
def query(self, payment_id: str) -> Dict[str, Any]:
return {"status": "paid", "payment_id": payment_id}
# ==================== 抽象工厂 ====================
class PaymentFactory(ABC):
"""支付工厂抽象接口"""
@abstractmethod
def create_gateway(self) -> PaymentGateway:
pass
# ==================== 具体工厂 ====================
class AlipayFactory(PaymentFactory):
def __init__(self, config: Dict[str, str]):
self.config = config
def create_gateway(self) -> PaymentGateway:
return AlipayGateway(
app_id=self.config["app_id"],
private_key=self.config["private_key"],
gateway_url=self.config["gateway_url"],
)
class WeChatPayFactory(PaymentFactory):
def __init__(self, config: Dict[str, str]):
self.config = config
def create_gateway(self) -> PaymentGateway:
return WeChatPayGateway(
mch_id=self.config["mch_id"],
api_key=self.config["api_key"],
cert_path=self.config["cert_path"],
)
class StripeFactory(PaymentFactory):
def __init__(self, config: Dict[str, str]):
self.config = config
def create_gateway(self) -> PaymentGateway:
return StripeGateway(
api_key=self.config["api_key"],
webhook_secret=self.config["webhook_secret"],
)
# ==================== 工厂注册中心 ====================
class PaymentFactoryRegistry:
"""工厂注册中心:统一管理所有支付工厂"""
_factories: Dict[str, PaymentFactory] = {}
@classmethod
def register(cls, payment_type: str, factory: PaymentFactory):
cls._factories[payment_type] = factory
@classmethod
def get_factory(cls, payment_type: str) -> PaymentFactory:
factory = cls._factories.get(payment_type)
if factory is None:
raise ValueError(f"不支持的支付方式: {payment_type}")
return factory
# ==================== 业务层:客户端调用 ====================
class PaymentService:
"""支付服务:业务层只依赖抽象,不依赖具体实现"""
def pay(self, payment_type: str, amount: float, order_id: str) -> Dict[str, Any]:
factory = PaymentFactoryRegistry.get_factory(payment_type)
gateway = factory.create_gateway()
return gateway.pay(amount, order_id)
def refund(self, payment_type: str, payment_id: str, amount: float) -> Dict[str, Any]:
factory = PaymentFactoryRegistry.get_factory(payment_type)
gateway = factory.create_gateway()
return gateway.refund(payment_id, amount)
# ==================== 应用启动时注册 ====================
def bootstrap_payment():
"""应用启动时注册所有支付方式"""
config = {
"alipay": {
"app_id": "2021000000000000",
"private_key": "/path/to/private.key",
"gateway_url": "https://openapi.alipay.com/gateway.do",
},
"wechat": {
"mch_id": "1600000000",
"api_key": "your_api_key",
"cert_path": "/path/to/cert.pem",
},
"stripe": {
"api_key": "sk_live_xxxxxx",
"webhook_secret": "whsec_xxxxxx",
},
}
PaymentFactoryRegistry.register("alipay", AlipayFactory(config["alipay"]))
PaymentFactoryRegistry.register("wechat", WeChatPayFactory(config["wechat"]))
PaymentFactoryRegistry.register("stripe", StripeFactory(config["stripe"]))
# ==================== 运行示例 ====================
if __name__ == "__main__":
bootstrap_payment()
service = PaymentService()
# 国内用户用支付宝
result1 = service.pay("alipay", 99.9, "ORDER_001")
print(result1)
# 海外用户用Stripe
result2 = service.pay("stripe", 14.99, "ORDER_002")
print(result2)
# 退款
refund_result = service.refund("alipay", "alipay_ORDER_001", 99.9)
print(refund_result)
7.3 这个设计好在哪里
第一,业务层零感知。 PaymentService的pay方法里没有任何if-else,它只做三件事:从注册中心拿工厂、用工厂创建网关、调用网关的pay方法。加新支付方式,业务代码一行都不用改。
第二,配置集中管理。 所有支付渠道的配置都在bootstrap_payment里统一加载,从配置文件或环境变量读取,不会散落在代码各处。
第三,可测试性强。 写单元测试时,可以注册一个MockPaymentFactory,返回一个模拟网关,不需要真实调用第三方支付接口。
第四,扩展性极好。 将来要加PayPal,只需要写PayPalGateway和PayPalFactory两个类,然后在启动时注册一下,整个系统就支持PayPal了。
八、常用场景总结
工厂模式在实际开发中应用非常广泛,以下是最常见的使用场景:
| 场景 | 说明 | 推荐模式 |
|---|---|---|
| 支付网关 | 支付宝、微信、Stripe、PayPal等多种支付方式 | 工厂方法 |
| 消息队列连接器 | Kafka、RabbitMQ、RocketMQ等不同MQ的连接创建 | 工厂方法 |
| 日志处理器 | 按级别输出到控制台、文件、远程服务器 | 简单工厂 |
| 数据库连接 | MySQL、PostgreSQL、MongoDB等不同数据库的连接 | 工厂方法 |
| 缓存适配器 | Redis、Memcached、本地缓存等不同缓存实现 | 工厂方法 |
| 配置解析器 | JSON、YAML、XML、INI等不同格式的解析 | 简单工厂 |
| 插件系统 | 动态加载和创建不同类型的插件实例 | 工厂方法+注册机制 |
| 对象池管理 | 统一创建和管理可复用对象(如线程池、连接池) | 简单工厂 |
| 序列化器 | JSON、MessagePack、Protobuf等不同序列化方式 | 简单工厂 |
| 通知发送器 | 邮件、短信、推送、钉钉、飞书等通知渠道 | 工厂方法 |
选择建议:
- 产品类型少、基本不变化 → 简单工厂,够用就行
- 产品类型多、需要频繁扩展 → 工厂方法,面向未来
- 不要为了用模式而用模式,简单场景直接new也没问题

九、面试官高频面试题
面试题一:简单工厂和工厂方法的区别是什么?
参考答案:
核心区别在于工厂的数量和扩展性。
简单工厂只有一个工厂类,负责创建所有产品,通过参数区分创建哪种产品。优点是结构简单,缺点是新增产品需要修改工厂类,违反开闭原则。
工厂方法有一个抽象工厂接口和多个具体工厂类,每个具体工厂只负责创建一种产品。优点是新增产品只需新增工厂类,符合开闭原则,缺点是类的数量会成倍增加。
一句话总结:简单工厂是"一个工厂造所有",工厂方法是"一个工厂造一种"。
面试题二:工厂模式解决了什么问题?
参考答案:
工厂模式主要解决了三个问题:
- 解耦问题:将对象的创建与使用分离,客户端不依赖具体产品类,只依赖抽象接口。
- 扩展性问题:通过工厂方法模式,新增产品时不需要修改已有代码,符合开闭原则。
- 复杂度问题:将复杂的对象创建逻辑封装在工厂内部,客户端调用简单,代码更清晰。
面试题三:简单工厂违反了什么设计原则?怎么改进?
参考答案:
简单工厂违反了两个设计原则:
- 开闭原则:新增产品时需要修改工厂类的if-else或字典映射。
- 单一职责原则:一个工厂类承担了所有产品的创建职责。
改进方式有两种:
- 用注册机制优化简单工厂,新增产品时调用注册方法而不是修改工厂内部代码,部分缓解开闭原则问题。
- 升级为工厂方法模式,每个产品对应一个工厂,完全符合开闭原则和单一职责原则。
面试题四:什么时候用简单工厂,什么时候用工厂方法?
参考答案:
根据产品的复杂度和变化频率来选择:
- 简单工厂适用于产品类型较少、相对稳定、创建逻辑简单的场景。比如日志级别处理器、配置文件解析器等。
- 工厂方法适用于产品类型较多、需要频繁扩展、每种产品创建逻辑复杂的场景。比如支付网关、消息队列连接器、插件系统等。
判断标准很简单:如果你预计这个产品家族未来会频繁新增类型,就直接上工厂方法;如果产品类型基本固定,简单工厂就够了。
面试题五:工厂方法模式的缺点是什么?
参考答案:
工厂方法模式的主要缺点是类爆炸。每新增一个产品,就需要同时新增一个产品类和一个工厂类,类的数量成倍增长。当产品类型非常多时,系统中会存在大量的工厂类,增加了代码的复杂度和理解成本。
此外,工厂方法模式引入了抽象层,对于初学者来说理解门槛更高,在简单场景下可能属于过度设计。
面试题六:Python中实现工厂模式有哪些优雅的写法?
参考答案:
Python作为动态语言,实现工厂模式比Java更灵活:
- 字典映射:用字典替代if-else,key是产品标识,value是产品类。
- 注册机制:提供register方法,支持动态注册新产品类,不需要修改工厂内部代码。
- 类装饰器:用装饰器自动注册产品类,写法更优雅。
- 元类:通过元类在类定义时自动注册,实现零配置注册。
- 函数作为工厂:Python中函数是一等公民,可以直接用函数作为工厂,不需要定义类。
示例:用装饰器实现自动注册的简单工厂。
python
class PhoneFactory:
_registry = {}
@classmethod
def register(cls, brand_name):
def wrapper(brand_cls):
cls._registry[brand_name] = brand_cls
return brand_cls
return wrapper
@classmethod
def create(cls, brand_name, model, color):
brand_cls = cls._registry.get(brand_name)
if brand_cls is None:
raise ValueError(f"未知品牌: {brand_name}")
return brand_cls(model, color)
@PhoneFactory.register("Apple")
class Apple(BasePhone):
pass
@PhoneFactory.register("Huawei")
class Huawei(BasePhone):
pass
十、总结
工厂模式是创建型设计模式中最基础、最常用的一种。它的核心思想就一句话:把对象的创建交给工厂,客户端只负责使用。
普通工厂模式包含两种形态:
- 简单工厂:一个工厂造所有产品,简单直接但扩展性差,适合产品少且稳定的场景。
- 工厂方法:一个工厂造一种产品,扩展性好但类多,适合产品多且频繁扩展的场景。
从直接new到简单工厂再到工厂方法,这是一个逐步解耦、逐步符合设计原则的演进过程。但没有最好的模式,只有最合适的模式。在实际开发中,要根据业务复杂度和变化频率来选择,不要为了用模式而用模式。
掌握工厂模式,你就能写出更易扩展、更易维护、更易测试的代码。下次再遇到if-else创建对象的场景,不妨想想:是不是该用工厂模式重构一下了?
转载声明:本文为原创文章,如需转载,请联系作者获得授权,并注明出处。