《精通Python设计模式》试读:1.2 抽象工厂

抽象工厂设计模式是抽象方法的一种泛化。概括来说,一个抽象工厂是(逻辑上的)一组工厂方法,其中的每个工厂方法负责产生不同种类的对象(请参考[Eckel08,第193页])。 1.2.1 现实生活的例子 汽车制造业应用了抽象工厂的思想。冲压不同汽车模型的部件(车门、仪表盘、车篷、挡泥板及反光镜等)所使用的机件是相同的。机件装配起来的模型随时可配置,且易于改变。从下图我们能看到汽车制造业抽象工厂的一个例子,该图由www.sourcemaking.com提供(请参考网页[t.cn/RqB1tZS])。 1.2.2 软件的例子 程序包django_factory是一个用于在测试中创建Django模型的抽象工厂实现,可用来为支持测试专有属性的模型创建实例。这能让测试代码的可读性更高,且能避免共享不必要的代码,故有其存在的价值(请参考网页[t.cn/RqBBvcw])。 1.2.3 应用案例 因为抽象工厂模式是工厂方法模式的一种泛化,所以它能提供相同的好处:让对象的创建更容易追踪;将对象创建与使用解耦;提供优化内存占用和应用性能的潜力。 这样会产生一个问题:我们怎么知道何时该使用工厂方法,何时又该使用抽象工厂?答案是,通常一开始时使用工厂方法,因为它更简单。如果后来发现应用需要许多工厂方法,那么将创建一系列对象的过程合并在一起更合理,从而最终引入抽象工厂。 抽象工厂有一个优点,在使用工厂方法时从用户视角通常是看不到的,那就是抽象工厂能够通过改变激活的工厂方法动态地(运行时)改变应用行为。一个经典例子是能够让用户在使用应用时改变应用的观感(比如,Apple风格和Windows风格等),而不需要终止应用然后重新启动(请参考[GOF95,第99页])。 1.2.4 实现 为演示抽象工厂模式,我将重新使用Python 3 Patterns & Idioms(Bruce Eckel著)一书中的一个例子(请参考[Eckel08,第193页]),它是我个人最喜欢的例子之一。想象一下,我们正在创造一个游戏,或者想在应用中包含一个迷你游戏让用户娱乐娱乐。我们希望至少包含两个游戏,一个面向孩子,一个面向成人。在运行时,基于用户输入,决定该创建哪个游戏并运行。游戏的创建部分由一个抽象工厂维护。 从孩子的游戏说起,我们将该游戏命名为FrogWorld。主人公是一只青蛙,喜欢吃虫子。每个英雄都需要一个好名字,在我们的例子中,这个名字在运行时由用户给定。方法interact_with()用于描述青蛙与障碍物(比如,虫子、迷宫或其他青蛙)之间的交互,如下所示。 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是一个抽象工厂,其主要职责是创建游戏的主人公和障碍物。区分创建方法并使其名字通用(比如,make_character()和make_obstacle()),这让我们可以动态改变当前激活的工厂(也因此改变了当前激活的游戏),而无需进行任何代码变更。在一门静态语言中,抽象工厂是一个抽象类/接口,具备一些空方法,但在Python中无需如此,因为类型是在运行时检测的(请参考[Eckel08,第195页]和网页[t.cn/h47Rs9]),如下所示。 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() WizardWorld游戏也类似。在故事中唯一的区别是男巫战怪兽(如兽人)而不是吃虫子! 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() 类GameEnvironment是我们游戏的主入口。它接受factory作为输入,用其创建游戏的世界。方法play()则会启动hero和obstacle之间的交互,如下所示。 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()提示用户提供一个有效的年龄。如果年龄无效,则会返回一个元组,其第一个元素设置为False。如果年龄没问题,元素的第一个元素则设置为True,但我们真正关心的是元素的第二个元素,也就是用户提供的年龄,如下所示。 def validate_age(name): try: age = input('Welcom {}. 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) 最后一个要点是main()函数,该函数请求用户的姓名和年龄,并根据用户的年龄决定该玩哪个游戏,如下所示。 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() 抽象工厂实现的完整代码(abstract_factory.py)如下所示。 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 = 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() 该程序的一个样例输出如下所示。 >>> python3 abstract_factory.py Hello. What's your name? Nick Welcome Nick. How old are you? 17 ------ Frog World ------- Nick the Frog encounters a bug and eats it! 来尝试扩展一下这个游戏使其更完整吧。你可以随意添加障碍物、敌人以及其他任何想要的东西。

>精通Python设计模式

精通Python设计模式
作者: [荷] Sakis Kasampalis
isbn: 7115428034
书名: 精通Python设计模式
页数: 144
译者: 夏永锋
定价: 45.00元
出版社: 人民邮电出版社
装帧: 平装
出版年: 2016-7