工厂模式
在工厂设计模式中,客户端可以请求一个对象,而无需要知道这个对象来自哪里,也就是使用哪个类来生成这个对象。工厂背后的思想是简化对象的创建。与客户端自己基于类实例化直接创建对象相比,基于一个中心化函数来实现,更易于追踪创建了那些对象。
工厂通常有两种形式:一种是工厂方法,它是一个方法(函数),对不同的输入参数返回不同的对象;第二种是抽象工厂,它是一组用于创建一系列相关事物对象的工厂方法。
工厂方法
在工厂方法模式中,我们执行单个函数,传入一个参数(提供信息表明我们想要什么),但不要求直到任何关于对象如何实现以及对象来自哪里的细节。
现实生活中的例子:塑料玩具制造制造塑料玩具的压塑粉都是一样的,但是使用不同塑料模具就能产出不同的外形。比如有一个工厂方法,输入是目标外形的名称,输出则是要求的塑料外形。
软件中的例子:Django框架使用工厂方法模式来创建表单字段。Django的forms模块支持不同种类字段(CharField,EmailField)的创建和制定(max_length,required)。
应用案例:
如果因为应用创建对象的代码分布在多个不同的地方,而不是仅在一个函数/方法中,你发现没法跟踪这些对象,那么应该考虑使用工厂方法模式。工厂方法集中地在一个地方创建对象,使对象跟踪变得更容易。
案例实现
以下例子将关注两种流行的人类可读文件格式:XML和JSON。
在当前这个问题中,我们有一些输入数据存储在一个XML文件和JSON文件中,要对这两个文件进行解析,获取一些信息。同时,希望对这些(以及将来涉及的的所有)外部服务进行集中式的客户端连接。我们使用工厂方法来解决这个问题。
#数据来源: json文件:http://opensource.adobe.com/Spry/samples/data_region/JSONDataSetSample.html#Example4 xml文件:https://en.wikipedia.org/wiki/JSON#XML
import xml.etree.ElementTree as etree import json #类JSONConnector解析JSON文件,通过parsed_data()方法以一个字典dict的形式返回数据 class JSONConnector: def __init__(self,filepath): self.data = {} with open(filepath,mode='r',encoding='utf-8') as f: self.data = json.load(f) @property def parsed_data(self): return self.data #类XMLConnector解析XML文件,通过parsed_data()方法以xml.etree.Element列表的形式返回所有数据。 class XMLConnector: def __init__(self,filepath): self.tree = etree.parse(filepath) @property def parsed_data(self): return self.tree #工厂方法connect_factory,基于输入路径的扩展名返回一个JSONConnector或XMLConnector的实例 def connect_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) #函数connnect_to对工厂方法进行包装,添加了异常处理 def connect_to(filepath): factory = None try: factory = connect_factory(filepath) except ValueError as e: print(e) return factory def main(): sqlite_factory = connect_to('person.sq3') print() xml_factory = connect_to('test2.xml') xml_data = xml_factory.parsed_data liars = xml_data.findall(".//{}[{}='{}']".format('person','lastName','Liar')) for liar in liars: print('first name:{}'.format(liar.find('firstName').text)) print('last name:{}'.format(liar.find('lastName').text)) [print('phone number ({}):'.format(p.text)) for p in liar.find('phoneNumber')]#待完善XML模式匹配语法 print() json_factory = connect_to('test1.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'])) [print('topping:{}{}'.format(t['id'],t['type'])) for t in donut['topping']] if __name__ == '__main__': main()
抽象工厂
抽象工厂设计模式是抽象方法的一种泛化。概括来说,一个抽象工厂是(逻辑上的)一组工厂方法,其中每个工厂方法负责产生不同种类的对象。
现实生活中的例子:汽车制造业应用了抽象工厂的思想。冲压不同汽车模型的部件(车门、仪表盘、车篷以及挡泥板的等)所使用的的机件是相同的。机件组装起来的模型随时可配置,且易于改变。
软件中的例子:程序包django_factory是一个用于在测试中创建Django模型的抽象工厂实现,可用来为支持测试专有属性的模型创建实例。这能让测试代码的可读性更高,且能避免共享不必要的代码,故有其存在的价值。
应用案例:
抽象工厂模式是工厂方法模式的一种泛化,所以它能提供相同的好处:让对象的创建更容易追踪;将对象创建与使用解耦;提供优化内存占用和应用性能的潜力。
那我们何时使用工厂方法,何时又该使用抽象工厂?答案是,通常一开始使用工厂方法,因为它更简单。如果后来发现应用需要许多工厂方法,那么会将创建一些列对象的过程合并在一起更合理,从而最终引入抽象工厂。
#想象一下,我们正在创造一个游戏,或者想在应用中包含一个迷你游戏让用户娱乐一下。我们希望至少包含两个游戏,一个面向孩子,一个面向大人。在运行时,基于用户输入,决定该创建那个游戏并运行。游戏的创建部分由一个抽象工厂维护。 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' #抽象工厂FrogWorld,其主要职责是创建游戏的主人公和障碍物。 class FrogWorld: def __init__(self,name): print(self) self.player_name = name def __str__(self): return ' ------ 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 'a evil ork' def action(self): return 'kills it' #抽象工厂WizardWorld,类似于FrogWorld class WizardWorld: def __init__(self,name): print(self) self.player_name = name def __str__(self): return ' ------ Wizard World ------' def make_character(self): return Wizard(self.player_name) def make_obstacle(self): return Ork() #类GameEnvironment是我们游戏的主入口,它接受factory作为输入,用其创建游戏的世界。 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) #validate_age提示用户输入一个有效的年龄 def validate_age(name): try: age = input('Welcome {}.How old are you?'.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 = input("Hello,What's your name?") valid_input = False while not valid_input: valid_input,age = validate_age(name) game = FrogWorld if age<18 else WizardWorld environment = GameEnvironment(game(name)) environment.play() if __name__ == '__main__': main()
小结
工厂方法设计模式的实现是一个不属于任何类的单一函数,负责单一种类对象的创建。
抽象工厂设计模式的实现是同属于单个类的许多个工厂方法用于创建一系列种类的相关对象。