在工厂设计模式中,客户端可以请求一个对象,而无需知道这个对象来自哪里;也就是,使用哪个类类生成这个对象。工厂背后的思想是简化对象的创建。与客户端自己基于类实例化直接创建对象相比,基于一个中心化函数来实现,更易于追踪创建了哪些对象。通过将创建对象的代码和使用对象的代码解耦,工厂能够降低应用维护的复杂度。
工厂通常有两种形式:一种是工厂方法,它是一个方法(或是一个函数),对不同的输入参数返回不同的对象;第二种是抽象工厂,它是一组创建一系列相关事物对象的工厂方法。
1. 工厂方法
在工厂方法模式中,我们执行单个函数,传入一个参数(提供信息表明我们想要什么),但并不要求知道任何关于对象如何实现以及对象来自哪里的细节。
1.1 案例
以下例子将使用工厂方法来解析两种流行的人类可读文件格式:XML和JSON。我们使用Python发型版本自带的两个库(xml.etree.ElementTree和json)来处理XML和JSON,如下所示:
import xml.etree.ElementTree as etree import json
下面是工厂方法实现(factory_method.py)的完整代码。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2018/7/13 15:34 # @Author : Yaheng Wang (m13262578991@163.com) # @Link : http://www.wy2160640.github.io # @Version : 1.0 import xml.etree.ElementTree as etree import json import io class JSONConnector: def __init__(self, filepath): self.data = {} with io.open(filepath, mode='r', encoding='utf-8') as f: self.data = json.load(f) @property def parsed_data(self): return self.data class XMLConnector: def __init__(self, filepath): self.tree = etree.parse(filepath) @property def parsed_data(self): return self.tree def connection_factory(filepath): if filepath.endswith('json'): connector = JSONConnector elif filepath.endswith('xml'): connector = XMLConnector else: raise ValueError('Cannot connect to {}'.format(filepath)) return connector(filepath) def connect_to(filepath): factory = None try: factory = connection_factory(filepath) except ValueError as ve: print(ve) return factory def main(): sqlite_factory = connect_to('data/person.sq3') print() xml_factory = connect_to('data/person.xml') xml_data = xml_factory.parsed_data liars = xml_data.findall(".//{}[{}='{}']".format('person', 'lastName', 'Liar')) print('found: {} persons'.format(len(liars))) for liar in liars: print('first name: {}'.format(liar.find('firstName').text)) print('last name: {}'.format(liar.find('lastName').text)) for p in liar.find('phoneNumbers'): print('phone number ({})'.format(p.attrib['type']), p.text) print() json_factory = connect_to('data/donut.json') json_data = json_factory.parsed_data print('found : {} donuts'.format(len(json_data))) for donut in json_data: print('name: {}'.format(donut['name'])) print('price: ${}'.format(donut['ppu'])) for t in donut['topping']: print('topping: {} {}'.format(t['id'], t['type'])) if __name__ == '__main__': main()
2. 抽象工厂
抽象工厂设计模式是抽象方法的一种泛化。概括来说,一个抽象工厂是(逻辑上的)一组工厂方法,其中的每个工厂方法负责产生不同种类的对象。
2.1案例
抽象工厂有一个优点,在使用工厂方法时从用户视角通常是看不到的,那就是抽象工厂能够通过改变激活的工厂方法动态地(运行时)改变用户行为。一个经典的例子是能够让用户在使用应用时改变应用的观感,而不需要终止应用然后重新启动。
想象一下,我们正在创造一个游戏,或者想在应用中包含一个迷你游戏让用户娱乐娱乐。我们希望至少包含两个游戏,一个面向孩子,一个面向成人。在运行时,基于用户输入,决定改创建哪个游戏并运行。游戏创建部分由一个抽象工厂维护。
抽象工厂实现的完整代码(abstract_factory.py)如下所示:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018/7/14 15:13
# @Author : Yaheng Wang (m13262578991@163.com)
# @Link : http://www.wy2160640.github.io
# @Version : 1.0
class Frog:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print('{} the Frog encounters {} and {}!'.format(self, obstacle, obstacle.action()))
class Bug:
def __str__(self):
return 'a bug'
def action(self):
return 'eats it'
class FrogWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Frog World ------'
def make_character(self):
return Frog(self.player_name)
def make_obstacle(self):
return Bug()
class Wizard:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print('{} the Wizard battles against {} and {}!'.format(self, obstacle, obstacle.action()))
class Ork:
def __str__(self):
return 'an evil ork'
def action(self):
return 'kills it'
class WizardWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Wizard World ------'
def make_character(self):
return Wizard(self.player_name)
def make_obstacle(self):
return Ork()
class GameEnvironment:
def __init__(self, factory):
self.hero = factory.make_character()
self.obstacle = factory.make_obstacle()
def play(self):
self.hero.interact_with(self.obstacle)
def validate_age(name):
try:
age = raw_input('Welcome {}. How old are you?\n'.format(name))
age = int(age)
except ValueError as err:
print("Age {} is invalid, please try again...".format(age))
return (False, age)
return (True, age)
def main():
name = raw_input("Hello. What's your name?\n")
valid_input = False
while not valid_input:
valid_input, age = validate_age(name)
game = FrogWorld if age < 18 else WizardWorld
enviroment = GameEnvironment(game(name))
enviroment.play()
if __name__ == '__main__':
main()
3.小结
两种模式都可以用于以下几种场景:(a)想要追踪对象的创建时,(b)想要将对象的创建与使用解耦时,(c)想要优化应用的性能和资源占用时。
工厂设计方法模式的实现是一个不属于任何类的单一函数,负责单一种类对象(一个形状、一个连接点或者其他对象)的创建。作为示例,我们实现了一个工厂方法,提供了访问XML和JSON文件的能力。
抽象工厂设计模式的实现是同属于单个类的许多个工厂方法用于创建一系列种类的相关对象(一辆车的部件、一个游戏的环境,或者其他对象)。作为抽象工厂实现的示例,我们完成了一个迷你游戏,演示了如何在单个类中使用多个相关工厂。