Jamal的博客

Python设计模式-工厂模式

工厂方法

在工厂方法模式中,我们执行单个函数,传入一个参数(提供信息表明我们想要什么),但并不要求知道任何关于对象如何实现以及对象来自哪里的细节

现实生活中的例子

现实中用到工厂方法模式思想的一个例子是塑料玩具制造。制造塑料玩具的压塑粉都是一样 的,但使用不同的塑料模具就能产出不同的外形。比如,有一个工厂方法,输入是目标外形(鸭 子或小车)的名称,输出则是要求的塑料外形。

软件中的例子

Django框架使用工厂方法模式来创建表单字段。Django的forms模块支持不同种类字段 (CharField、EmailField)的创建和定制(max_length、required)

应用案例

如果因为应用创建对象的代码分布在多个不同的地方,而不是仅在一个函数/方法中,你会比较难的跟踪这些对象的创建,那么应该考虑使用工厂方法模式,工厂方法集中地在一个地方创建对象,使对象跟踪变得更容易。注意,创建多个工厂方法也完全没有问 10 题,实践中通常也这么做,对相似的对象创建进行逻辑分组,每个工厂方法负责一个分组。例如, 有一个工厂方法负责连接到不同的数据库(MySQL、SQLite),另一个工厂方法负责创建要求的几何对象(圆形、三角形),等等。
若需要将对象的创建和使用解耦,工厂方法也能派上用场。创建对象时,我们并没有与某个 12 特定类耦合/绑定到一起,而只是通过调用某个函数来提供关于我们想要什么的部分信息。这意味着修改这个函数比较容易,不需要同时修改使用这个函数的代码。
另外一个值得一提的应用案例与应用性能及内存使用相关。工厂方法可以在必要时创建新的 对象,从而提高性能和内存使用率,若直接实例化类来创建对象, 那么每次创建新对象就需要分配额外的内存。

实现

我们来实现一个文件工厂,可以根据不同的文件名后缀创建可以处理不同类型的文件的实例。
假设我们现在有json和xml两类文件,那么就需要两个类,分别用来处理xml和json文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
JSONConnector.py:
class JSONConnector():
def __init__(self, file_path):
self.data = dict()
with open(file_path, mode='r', encoding='utf-8') as f:
print(f)
self.data = json.loads(f)
@property
def parsed_data(self):
return self.data
XMLConnector.py:
class XMLConnector():
def __init__(self, file_path):
self.tree = etree.parse(file_path)
@property
def parsed_data(self):
return self.tree

在这里对parsed_data使用property让这个方法看上去像变量一样。
下面我们写一个工厂函数:

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
def connect_factory(file_path):
if file_path.endswith("json"):
connector = JSONConnector
elif file_path.endswith("xml"):
connector = XMLConnector
else:
raise ValueError('cannot connect to {}'.format(file_path))
return connector(file_path)
def connect_to(file_path):
factory = None
try:
factory = connect_factory(file_path)
except ValueError as e:
print("aaaaaaaa")
print(e)
return factory
def main():
"""
xml_factory = connect_to("data/person.xml")
print(xml_factory)
xml_data = xml_factory.parsed_data
john = xml_data.findall(".//{person}[{lastName}='{}']".format("Smith"))
print("found: {} person".format(len(john)))
"""
json_factory = connect_factory("data/person.json")
print(json_factory)
json_data = json_factory.parsed_data
print(json_data)
if __name__ == "__main__":
main()

connect_factory通过判断文件的后缀名来判断生成哪个类的实例,connect_to处理了异常。