抽象工厂简介
抽象工厂设计模式是工厂方法的泛化。抽象工厂是(逻辑上的)一组工厂方法, 每个工厂方法负责产生不同种
类的对象。
在汽车制造中同样的机械用于冲压不同车型的零件(车门、面板、引擎盖、挡泥板和后视镜)。由机械组装出来的模型是可以配置的,便于随时更改。
factory_boy(https://github.com/FactoryBoy/factory_boy)包提供了抽象的工厂实现,用于在测试中创建Django模型。它用于创建支持测试特定属性的模型实例。这样测试可读性很好,而且避免了不必要的代码共享。
抽象工厂模式使跟踪对象的创建变得更容易,它使对象创建使用解耦这样我们有可能改善应用程序的内存使用和性能。
我们通常从工厂方法开始,因为它比较简单。如果应用程序需要许多工厂方法,将这些方法组合起来创建对象族是有意义的,这种需要使用抽象工厂。
抽象工厂使我们能够通过改变活动的工厂方法来动态地(在运行时)修改我们应用程序的行为。最经典的例子就是在应用程序使用时,能够为用户改变应用程序的外观和感觉(例如,类似苹果的、类似Windows的等等),而不需要重启。
抽象工厂实例
我们希望至少包含两个游戏,一个是儿童游戏,一个是成人游戏。我们将根据用户的输入来决定创建哪个游戏并在运行时启动。抽象工厂负责游戏创建。
让我们从儿童游戏开始。它的名字叫FrogWorld。主人公是一只喜欢吃虫子的青蛙。名字是由用户在运行时给出的。interact_with()方法用来描述青蛙与障碍物(例如虫子、谜题和其他青蛙)的交互,如下所示。
class Frog:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
act = obstacle.action()
msg = f'{self} the Frog encounters {obstacle} and {act}!'
print(msg)
障碍物只能是一只虫子。当青蛙遇到虫子时,只支持一个动作:吃掉。
class Bug:
def __str__(self):
return 'a bug'
def action(self):
return 'eats it'
FrogWorld类是抽象工厂。它创建游戏中的主要角色和障碍物。保持创建方法的分离和它们的名称的通用性(例如,make_character()和make_obstacle())允许我们在不修改任何代码的情况下动态地改变活动工厂(以及活动游戏)。在静态类型的语言中,抽象工厂应该是一个抽象的类/接口,并带有空的方法,但在Python中,这不是必需的,因为类型是在运行时检查的 (j.mp/ginstromdp)。代码如下。
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):
act = obstacle.action()
msg = f'{self} the Wizard battles against {obstacle} and {act}!'
print(msg)
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类是我们游戏的主要入口。它接受工厂作为输入,并使用它来创建游戏的世界。play()方法启动了创建的英雄和障碍物之间的交互,如下所示。
# Game environment
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,这时我们实际关心的是元组的第二个元素,也就是用户年龄。
# Game environment
def validate_age(name):
try:
age = input(f'Welcome {name}. How old are you? ')
age = int(age)
except ValueError as err:
print(f"Age {age} is invalid, please try again...")
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()
if __name__ == '__main__':
main()