1. 写在前面
我们都知道,设计模式是一组最佳实践,可用于解决软件开发中反复出现的问题。在本文中,我们将介绍另一种设计模式——工厂模式。
公众号: 滑翔的纸飞机
2. 工厂模式
2.1 介绍
工厂模式是由一个工厂对象根据不同参数创建不同的实例。具体传什么参数,创建什么实例的逻辑是在工厂对象中完成的。简单点就是:在不指定确切类的情况下创建对象。
优点:
只需要传入一个正确的参数,就可以获取你所需要的对象而无需知道其创建细节。
缺点:
工厂一旦需要生产新产品就需要修改工厂类的方法逻辑,违背了开放—封闭原则; 如果产品实例种类很多,也导致了工厂内部逻辑复杂,不易维护。
为避免工厂类因业务复杂代码庞大,可以拆分成一个个的工厂类,这样代码就不会都耦合在同一个类里了,进一步降低程序的耦合性。
2.2 基本实现
通过一个简单例子,看看工厂模式如何在Python中实现;
示例场景:根据用户输入创建不同类型的动物;
"""
@Time:2023/9/20 00:58
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""
class Animal:
    def say(self):
        pass
class Dog(Animal):
    def say(self):
        return "Woof!"
class Cat(Animal):
    def say(self):
        return "Meow!"
# 工厂
class AnimalFactory:
    def create_animal(self, animal_type):
        if animal_type == "dog":
            return Dog()
        elif animal_type == "cat":
            return Cat()
        else:
            raise ValueError("Invalid animal type")
# 入口
if __name__ == '__main__':
    factory = AnimalFactory()
    animal1 = factory.create_animal("dog")
    animal2 = factory.create_animal("cat")
- 类
Animal定义为父类,包含一个方法,用来表示动物叫声。同时定义了2个子类(Dog、Cat)继承该父类,覆盖此方法,以返回子类所定义动物的叫声; AnimalFactory定义为工厂类,同时包含一个方法create_animal,该方法通过传入参数animal_type返回相应动物实例对象;- 在
__main__入口,创建一个工厂类实例,并通过类方法创建两种不同类型的动物; 
如上示例就是一个简单的工厂模式。
2.3 如何工作?
在工厂模式中,有一个 Factory 类,它定义了用于创建对象的方法。允许客户端代码在不知道将要创建对象的确切类型情况下创建对象。
基本流程如下:
- 客户端在 Factory 接口上调用工厂方法。
 - 创建一个具体类型的实例对象并将其返回到客户端。
 - 客户端使用“具体类型”的实例对象。
 
要素:
(1)抽象产品类(Product):抽象方法&公共接口,产品子类具体实现及继承; (2)具体产品类(ConcreteProduct,继承抽象产品类):定义具体产品实现; (3)抽象工厂类(Factory):提供抽象方法; (4)具体工厂类(ConcreteFactory,继承抽象工厂类):定义创建具体产品实例的方法;
2.4 使用案例
(1)示例一:在复杂系统中创建对象任务时,工厂模式可能是一个有用的工具
对象创建可能是一项复杂的任务,尤其是在大型系统中,其中可能需要根据不同的配置或要求创建许多不同类型的对象。
手动创建对象可能非常耗时、容易出错,并且可能导致代码重复。这就是抽象工厂等设计模式有用的地方。
抽象工厂模式提供了一个接口,用于创建相关对象或依赖对象,而无需指定其具体类。
例如:假设你正在构建一个咖啡店应用程序,客户可以在其中订购不同类型的咖啡。每种咖啡都有一个名称、价格和配料。你可以将每种类型的咖啡表示为 Python 类,并定义一个基于其成分计算咖啡成本的方法。
"""
@Time:2023/9/21 00:52
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""
class Coffee:
    def __init__(self, name, price, ingredients):
        self.name = name  # 名称
        self.price = price  # 价格
        self.ingredients = ingredients  # 成分
    def cost(self):
        # 计算费用
        return sum([ingredient.cost() for ingredient in self.ingredients])
class Espresso(Coffee):
    # 浓缩咖啡
    def __init__(self):
        super().__init__('Espresso', 2.0, [CoffeeBean(), Water()])
class Latte(Coffee):
    # 拿铁咖啡
    def __init__(self):
        super().__init__('Latte', 3.0, [CoffeeBean(), Milk(), Water()])
类Coffee是定义咖啡基本属性的抽象类。Espresso 和 Latte是具体的子类,定义了每种咖啡的特定成分和价格。
现在,假设你要向应用程序添加一种新型咖啡,例如卡布奇诺咖啡。你需要创建该类的新子类,并定义卡布奇诺的特定成分和价格。
工厂模式实现:
class CoffeeFactory:
    def create_coffee(self, coffee_type):
        if coffee_type == 'espresso':
            return Espresso()
        elif coffee_type == 'latte':
            return Latte()
        else:
            raise ValueError(f"Invalid coffee type: {coffee_type}")
该类有一个方法(create_coffee),该方法接受一个参数并返回相应类型咖啡的新实例。现在,当你想在应用程序中创建新咖啡时,只需调用类的方法:
factory = CoffeeFactory()
espresso = factory.create_coffee('espresso')
latte = factory.create_coffee('latte')
(2)工厂模式的另一个用例:涉及创建多个对象实例的问题
假设你正在 Python 中构建一个简单的银行应用程序,并且你已经定义了一个类来表示银行帐户。该类具有当前存储帐户余额的属性,以及从帐户中存入和提取资金的方法:
class BankAccount:
    # 银行账户
    def __init__(self, balance=0):
        # 余额
        self.balance = balance
    def deposit(self, amount):
        # 存
        self.balance += amount
    def withdraw(self, amount):
        # 取
        if amount > self.balance:
            raise ValueError("Insufficient balance")
        self.balance -= amount
现在,假设你创建类的两个实例,每个实例用于不同客户:
customer1_account = BankAccount()
customer2_account = BankAccount()
如果两个客户都向他们的账户存入一些钱,你会期望他们的账户余额会相应地更新。例如:
customer1_account.deposit(100)
customer2_account.deposit(50)
print(customer1_account.balance)  # prints 100
print(customer2_account.balance)  # prints 50
但是,如果你不小心将一个帐户对象分配给另一个帐户对象,该怎么办?例如:
customer1_account = customer2_account
customer1_account.deposit(100)
print(customer2_account.balance)  # prints 100, not 50!
发生这种情况是因为两者现在都指向内存中的同一对象。因此,当你将钱存入时,你实际上是在更新共享对象的余额,这也会影响customer2_account.balance的余额。这可能会导致程序中出现意外行为和难以调试的错误。
若要避免此问题,可以使用像单例模式这样的设计模式来确保只创建类的一个实例,并在需要使用它的程序的之间共享。 这可以帮助你管理对象创建并防止与多个对象实例相关的问题。
下面是上述方案在工厂模式中的实现:
"""
@Time:2023/9/21 00:52
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""
class BankAccount:
    def __init__(self, balance=0):
        # 余额
        self.balance = balance
    def deposit(self, amount):
        # 存
        self.balance += amount
    def withdraw(self, amount):
        # 取
        if amount > self.balance:
            raise ValueError("Insufficient balance")
        self.balance -= amount
class BankAccountFactory:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.accounts = {}
        return cls._instance
    def create_account(self, account_number, balance=0):
        if account_number not in self.accounts:
            account = BankAccount(balance)
            self.accounts[account_number] = account
        else:
            account = self.accounts[account_number]
        return account
在此实现中,我们使用工厂模式来创建和管理类的实例。该类被设计为单例,因此程序中只有一个实例。该方法将帐号和余额作为参数,并返回一个对象。如果字典中已存在帐号,则该方法返回现有对象;否则,它会创建一个新字典并将其添加到字典中。
下面是如何使用类创建和管理对象的示例:
if __name__ == '__main__':
    factory = BankAccountFactory()
    account1 = factory.create_account('123')
    account2 = factory.create_account('456')
    account1.deposit(100)
    account2.deposit(50)
    print(account1.balance)  # prints 100
    print(account2.balance)  # prints 50
    account3 = factory.create_account('123')  # returns existing account
    account3.deposit(50)
    print(account1.balance)  # prints 150, not 50!
输出:
100
50
150
如你所见,确保每个对象只有一个实例,这可以防止与多个对象实例相关的问题。
(3)数据处理是工厂模式可能有用的另一种方案
例如:考虑数据处理,包括:清理、转换和加载。你可以创建一个工厂类,为每种类型的数据生成相应的步骤。
工厂类提供一种基于输入数据类型生成处理步骤的方法
以下是实现:
"""
@Time:2023/9/21 01:15
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""
class DataProcessor:
    # 数据处理器
    def process(self, data):
        pass
class CleanDataProcessor(DataProcessor):
    # 数据清理
    def process(self, data):
        # code to clean data
        return data
class TransformDataProcessor(DataProcessor):
    # 数据转换
    def process(self, data):
        # code to transform data
        return data
class LoadDataProcessor(DataProcessor):
    # 数据加载
    def process(self, data):
        # code to load data
        return data
class DataProcessorFactory:
    # 数据处理工厂
    @staticmethod
    def create_data_processor(processor_type):
        if processor_type == "clean":
            return CleanDataProcessor()
        elif processor_type == "transform":
            return TransformDataProcessor()
        elif processor_type == "load":
            return LoadDataProcessor()
        else:
            raise ValueError("Invalid processor type")
if __name__ == '__main__':
    data = [1, 2, 3, 4, 5]
    processor = DataProcessorFactory.create_data_processor("clean")
    data = processor.process(data)
    processor = DataProcessorFactory.create_data_processor("transform")
    data = processor.process(data)
    processor = DataProcessorFactory.create_data_processor("load")
    data = processor.process(data)
在此示例中,DataProcessor类充当不同类型的数据处理步骤(清理、转换和加载)的基类。DataProcessorFactory类是封装对象创建过程的工厂类,方法(create_data_processor)接受一个参数,并根据提供的类型返回对应实例。
3 最后
工厂是一种强大而灵活的设计模式,广泛用于软件开发。它提供了一种创建对象的方法,而无需指定将要创建的确切对象类,并将对象创建过程集中在一个位置。通过使用工厂方法,开发人员可以根据某些输入创建不同类型的对象,从而使代码更加灵活、可维护且不易出错。









