• Python: 开始使用工厂模式设计


    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")
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 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()])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    Coffee是定义咖啡基本属性的抽象类。EspressoLatte是具体的子类,定义了每种咖啡的特定成分和价格。

    现在,假设你要向应用程序添加一种新型咖啡,例如卡布奇诺咖啡。你需要创建该类的新子类,并定义卡布奇诺的特定成分和价格。

    工厂模式实现:

    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}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    该类有一个方法(create_coffee),该方法接受一个参数并返回相应类型咖啡的新实例。现在,当你想在应用程序中创建新咖啡时,只需调用类的方法:

    factory = CoffeeFactory()
    espresso = factory.create_coffee('espresso')
    latte = factory.create_coffee('latte')
    
    • 1
    • 2
    • 3

    (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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    现在,假设你创建类的两个实例,每个实例用于不同客户:

    customer1_account = BankAccount()
    customer2_account = BankAccount()
    
    • 1
    • 2

    如果两个客户都向他们的账户存入一些钱,你会期望他们的账户余额会相应地更新。例如:

    customer1_account.deposit(100)
    customer2_account.deposit(50)
    print(customer1_account.balance)  # prints 100
    print(customer2_account.balance)  # prints 50
    
    • 1
    • 2
    • 3
    • 4

    但是,如果你不小心将一个帐户对象分配给另一个帐户对象,该怎么办?例如:

    customer1_account = customer2_account
    customer1_account.deposit(100)
    print(customer2_account.balance)  # prints 100, not 50!
    
    • 1
    • 2
    • 3

    发生这种情况是因为两者现在都指向内存中的同一对象。因此,当你将钱存入时,你实际上是在更新共享对象的余额,这也会影响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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    在此实现中,我们使用工厂模式来创建和管理类的实例。该类被设计为单例,因此程序中只有一个实例。该方法将帐号和余额作为参数,并返回一个对象。如果字典中已存在帐号,则该方法返回现有对象;否则,它会创建一个新字典并将其添加到字典中。

    下面是如何使用类创建和管理对象的示例:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如你所见,确保每个对象只有一个实例,这可以防止与多个对象实例相关的问题。

    (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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    在此示例中,DataProcessor类充当不同类型的数据处理步骤(清理、转换和加载)的基类。DataProcessorFactory类是封装对象创建过程的工厂类,方法(create_data_processor)接受一个参数,并根据提供的类型返回对应实例。

    3 最后

    工厂是一种强大而灵活的设计模式,广泛用于软件开发。它提供了一种创建对象的方法,而无需指定将要创建的确切对象类,并将对象创建过程集中在一个位置。通过使用工厂方法,开发人员可以根据某些输入创建不同类型的对象,从而使代码更加灵活、可维护且不易出错。

    感谢您花时间阅读文章
    关注公众号不迷路:)
  • 相关阅读:
    贪心算法--装箱问题
    Dubbo之启动时检查(check属性)。
    Gbase8s数据库ALTER INDEX 语句
    SA8650 camera UsecaseId UsecaseAuto
    学redis看这里就行了
    始祖双碳新闻 | 2022年8月8日碳中和行业早知道
    物理层课后作业
    JS生成器的介绍
    Linux内核 -- 汇编结合ko案例之PMU获取周期技术
    Springboot集成kafka高级应用实战
  • 原文地址:https://blog.csdn.net/u011521019/article/details/133239928