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