02、Python普通工厂模式

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

文章目录

一、痛点场景:你是不是也在写这样的代码?

在开始讲工厂模式之前,先来看一段几乎每个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 这个设计好在哪里

第一,业务层零感知。 PaymentServicepay方法里没有任何if-else,它只做三件事:从注册中心拿工厂、用工厂创建网关、调用网关的pay方法。加新支付方式,业务代码一行都不用改。

第二,配置集中管理。 所有支付渠道的配置都在bootstrap_payment里统一加载,从配置文件或环境变量读取,不会散落在代码各处。

第三,可测试性强。 写单元测试时,可以注册一个MockPaymentFactory,返回一个模拟网关,不需要真实调用第三方支付接口。

第四,扩展性极好。 将来要加PayPal,只需要写PayPalGatewayPayPalFactory两个类,然后在启动时注册一下,整个系统就支持PayPal了。


八、常用场景总结

工厂模式在实际开发中应用非常广泛,以下是最常见的使用场景:

场景 说明 推荐模式
支付网关 支付宝、微信、Stripe、PayPal等多种支付方式 工厂方法
消息队列连接器 Kafka、RabbitMQ、RocketMQ等不同MQ的连接创建 工厂方法
日志处理器 按级别输出到控制台、文件、远程服务器 简单工厂
数据库连接 MySQL、PostgreSQL、MongoDB等不同数据库的连接 工厂方法
缓存适配器 Redis、Memcached、本地缓存等不同缓存实现 工厂方法
配置解析器 JSON、YAML、XML、INI等不同格式的解析 简单工厂
插件系统 动态加载和创建不同类型的插件实例 工厂方法+注册机制
对象池管理 统一创建和管理可复用对象(如线程池、连接池) 简单工厂
序列化器 JSON、MessagePack、Protobuf等不同序列化方式 简单工厂
通知发送器 邮件、短信、推送、钉钉、飞书等通知渠道 工厂方法

选择建议

  • 产品类型少、基本不变化 → 简单工厂,够用就行
  • 产品类型多、需要频繁扩展 → 工厂方法,面向未来
  • 不要为了用模式而用模式,简单场景直接new也没问题

九、面试官高频面试题

面试题一:简单工厂和工厂方法的区别是什么?

参考答案

核心区别在于工厂的数量和扩展性。

简单工厂只有一个工厂类,负责创建所有产品,通过参数区分创建哪种产品。优点是结构简单,缺点是新增产品需要修改工厂类,违反开闭原则。

工厂方法有一个抽象工厂接口和多个具体工厂类,每个具体工厂只负责创建一种产品。优点是新增产品只需新增工厂类,符合开闭原则,缺点是类的数量会成倍增加。

一句话总结:简单工厂是"一个工厂造所有",工厂方法是"一个工厂造一种"。

面试题二:工厂模式解决了什么问题?

参考答案

工厂模式主要解决了三个问题:

  1. 解耦问题:将对象的创建与使用分离,客户端不依赖具体产品类,只依赖抽象接口。
  2. 扩展性问题:通过工厂方法模式,新增产品时不需要修改已有代码,符合开闭原则。
  3. 复杂度问题:将复杂的对象创建逻辑封装在工厂内部,客户端调用简单,代码更清晰。

面试题三:简单工厂违反了什么设计原则?怎么改进?

参考答案

简单工厂违反了两个设计原则:

  1. 开闭原则:新增产品时需要修改工厂类的if-else或字典映射。
  2. 单一职责原则:一个工厂类承担了所有产品的创建职责。

改进方式有两种:

  • 注册机制优化简单工厂,新增产品时调用注册方法而不是修改工厂内部代码,部分缓解开闭原则问题。
  • 升级为工厂方法模式,每个产品对应一个工厂,完全符合开闭原则和单一职责原则。

面试题四:什么时候用简单工厂,什么时候用工厂方法?

参考答案

根据产品的复杂度和变化频率来选择:

  • 简单工厂适用于产品类型较少、相对稳定、创建逻辑简单的场景。比如日志级别处理器、配置文件解析器等。
  • 工厂方法适用于产品类型较多、需要频繁扩展、每种产品创建逻辑复杂的场景。比如支付网关、消息队列连接器、插件系统等。

判断标准很简单:如果你预计这个产品家族未来会频繁新增类型,就直接上工厂方法;如果产品类型基本固定,简单工厂就够了。

面试题五:工厂方法模式的缺点是什么?

参考答案

工厂方法模式的主要缺点是类爆炸。每新增一个产品,就需要同时新增一个产品类和一个工厂类,类的数量成倍增长。当产品类型非常多时,系统中会存在大量的工厂类,增加了代码的复杂度和理解成本。

此外,工厂方法模式引入了抽象层,对于初学者来说理解门槛更高,在简单场景下可能属于过度设计。

面试题六:Python中实现工厂模式有哪些优雅的写法?

参考答案

Python作为动态语言,实现工厂模式比Java更灵活:

  1. 字典映射:用字典替代if-else,key是产品标识,value是产品类。
  2. 注册机制:提供register方法,支持动态注册新产品类,不需要修改工厂内部代码。
  3. 类装饰器:用装饰器自动注册产品类,写法更优雅。
  4. 元类:通过元类在类定义时自动注册,实现零配置注册。
  5. 函数作为工厂: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创建对象的场景,不妨想想:是不是该用工厂模式重构一下了?


转载声明:本文为原创文章,如需转载,请联系作者获得授权,并注明出处。

相关推荐
Dontla1 小时前
VSCode Python扩展自动加载.env环境变量机制(Python插件)python.terminal.useEnvFile
ide·vscode·python
爱奥尼欧1 小时前
14.输出解析器-Pydantic与JSON
人工智能·学习·langchain·json
ReleaseU1 小时前
Claude Code 两天三版本、Cursor 把仓库搬进编辑器:AI 编程工具在卷什么?
人工智能·大模型
tachibana21 小时前
初识智能体
人工智能·ai·大模型·llm·agent
找方案1 小时前
Seedance 2.5突破30秒视频生成,AI视频赛道进入工业化时代
大数据·人工智能·字节跳动·可灵·ai视频生成·seedance2.5·工业化时代
码云骑士1 小时前
115-多模态大模型-GPT-4V-Gemini-Qwen-VL-能力全景
python
IT_陈寒1 小时前
Python的列表拷贝坑得我原地打转
前端·人工智能·后端
空堂与归1 小时前
如何用Python实现深度学习?PyTorch框架核心与实践
pytorch·python·深度学习
deepdata_cn1 小时前
从弱AI到强AI:符号推理是实现通用人工智能AGI的关键钥匙
人工智能·agi·符号推理