设计模式一:创建型模式
1 工厂方法(Factory Method)
工厂方法根据传入的参数不同,集中在同一个地方创建对象,使这些对象方便管理和追踪。例如Django使用工厂方法来创建表单字段CharField, DateField, TextField等,例如一个工厂方法负责根据不同参数连接到不同的数据库(MySQL, SQLite,Redis等)
使用工厂方法实现解析不同文件类型的数据:
import json
from xml.etree import ElementTree as tree
class JsonConnector:
def __init__(self, file_path):
self._data = dict()
with open(file_path, 'r', encoding='utf-8') as fr:
self._data = json.load(fr)
@property
def parse_data(self):
return self._data
class XmlConnector:
def __init__(self, file_path):
self._data = tree.parse(file_path)
@property
def parse_data(self):
return self._data
def connection_factory(file_path):
if file_path.endswith('.json'):
connector = JsonConnector
elif file_path.endswith('.xml'):
connector = XmlConnector
else:
raise ValueError(f'can not process .{file_path.split(".")[-1]} format file.')
return connector(file_path)
def connect_file(file_path):
try:
factory = connection_factory(file_path)
except ValueError as ve:
factory = None
print(ve)
return factory
if __name__ == '__main__':
for file in ['soloman.html', 'hello.xml', 'world.json']:
print(file)
fac = connect_file(file)
if fac is not None:
print(fac.parse_data)
总结
工厂方法是一种较为简单的模式,其主要作用是创建一个对象实例,而不暴露创建逻辑的复杂性。它定义了一种工厂类,内部包含一个静态方法(本例子直接使用了函数connection_factory),该方法返回一个具体产品类 的实例。
使用场景:
- 当系统需要根据不同参数或条件创建不同的对象时,可以使用工厂方法模式。
- 当对象的创建过程较为简单,但需要多次使用相同逻辑创建对象时,也可以考虑使用工厂方法模式。
2 抽象工厂(Abstract Factory)
抽象工厂是一组抽象方法的集合,其中每个抽象方法分别创建不同的对象。
抽象工厂实现根据不同年龄段呈现不同游戏内容:
class Frog:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, enemy):
print(f'{self.name} the frog encounters {enemy}, and {enemy.action()}')
class Bug:
def __init__(self):
pass
def __str__(self):
return 'a bug'
def action(self):
return 'eats it'
class FrogWorld:
def __init__(self, player_name):
print(self)
self.player_name = player_name
def __str__(self):
return "\n\n====== The FrogWorld ======"
def make_hero(self):
return Frog(self.player_name)
def make_enemy(self):
return Bug()
class Wizard:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, enemy):
print(f"{self.name} the wizard battle against {enemy} and {enemy.action()}")
class Ork:
def __init__(self):
pass
def __str__(self):
return "an evil ork"
def action(self):
return "kills it"
class WizardWorld:
def __init__(self, player_name):
print(self)
self.player_name = player_name
def __str__(self):
return "\n\n====== The WizardWorld ======"
def make_hero(self):
return Wizard(self.player_name)
def make_enemy(self):
return Ork()
class GameEnvironment:
def __init__(self, factory):
self.hero = factory.make_hero()
self.enemy = factory.make_enemy()
def play(self):
self.hero.interact_with(self.enemy)
def validate_player_age(age):
player_age = None
try:
player_age = int(age)
if player_age < 12 or player_age > 60:
raise ValueError("Age must between 12 and 60: your input exceed this range.")
except ValueError as ve:
print(f"{age} is not a valid age: {ve}")
return False, player_age
return True, player_age
def main():
user_name = input("Please input your nickname:")
flag = False
age = -1
while not flag:
user_age = input("Please input your age:")
flag, age = validate_player_age(user_age)
game = FrogWorld if age < 18 else WizardWorld
gm = GameEnvironment(game(user_name))
gm.play()
if __name__ == '__main__':
main()
总结
抽象工厂是一个更为复杂的模式,主要用于创建一组相关对象,而不是单个对象。它定义了一种抽象接口,包含多个具体产品类的工厂方法。
使用场景:
- 当系统需要根据不同参数或条件创建不同的对象组时,可以使用抽象工厂模式。
- 当对象的创建过程较为复杂,但需要考虑多个相关对象的创建逻辑时,也可以考虑使用抽象工厂模式。
3 建造者(Builder)
建造者模式是一种创建型设计模式,它将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式实现统一过程制造不同类型的披萨
from enum import Enum
import time
class PizzaProgress(Enum):
queued = "queued"
preparation = "preparation"
baking = "baking"
ready = "ready"
class PizzaDough(Enum):
thin = "thin"
thick = "thick"
class PizzaSauce(Enum):
tomato = "tomato"
creme_fraiche = "creme_fraiche"
class PizzaTopping(Enum):
mozzarella = "mozzarella"
bacon = "bacon"
ham = "ham"
mushrooms = "mushrooms"
red_onion = "red_onion"
STEP_DELAY = 1
class Pizza:
def __init__(self, name):
self.name = name
self.dough = None
self.sauce = None
self.topping = []
def __str__(self):
return self.name
def prepare_dough(self, dough):
self.dough = dough
print(f'prepared dough {self.dough.name} of your {self}')
time.sleep(STEP_DELAY)
print(f'done with the {self.dough.name} dough')
class MargaritaBuilder:
def __init__(self):
self.pizza = Pizza("margarita")
self.progress = PizzaProgress.queued
self.baking_time = 2
def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thin)
def add_sauce(self):
self.pizza.sauce = PizzaSauce.tomato
time.sleep(STEP_DELAY)
def add_topping(self):
self.pizza.topping.extend([
PizzaTopping.mushrooms,
PizzaTopping.red_onion
])
time.sleep(STEP_DELAY)
def bake(self):
self.progress = PizzaProgress.baking
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
class CreamyBaconBuilder:
def __init__(self):
self.pizza = Pizza('creamy bacon')
self.progress = PizzaProgress.queued
self.baking_time = 3
def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thick)
def add_sauce(self):
self.pizza.sauce = PizzaSauce.creme_fraiche
time.sleep(STEP_DELAY)
def add_topping(self):
self.pizza.topping.extend([
PizzaTopping.mushrooms,
PizzaTopping.bacon,
PizzaTopping.red_onion,
PizzaTopping.ham
])
time.sleep(STEP_DELAY)
def bake(self):
self.progress = PizzaProgress.baking
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
class Waiter:
def __init__(self):
self.builder = None
def make_pizza(self, builder):
self.builder = builder
[step() for step in (
builder.prepare_dough,
builder.add_sauce,
builder.add_topping, builder.bake
)]
@property
def pizza(self):
return self.builder.pizza
def validate_style(builders):
try:
pizza_type = input("which kind of pizza do you want? "
"[m]argarita or [c]reamy bacon\n")
builder = builders[pizza_type]()
return True, builder
except KeyError as err:
return False, None
def main():
builders = dict(m=MargaritaBuilder, c=CreamyBaconBuilder)
valid_input = False
builder = None
while not valid_input:
valid_input, builder = validate_style(builders)
waiter = Waiter()
waiter.make_pizza(builder)
pizza = waiter.pizza
print(f"Enjoy your pizza {pizza}")
总结
建造者模式适用于对象结构复杂、需要灵活组装对象、创建过程和使用过程需要分离以及需要逐步构建对象的场景。通过使用建造者模式,可以简化对象的创建过程,提高代码的可读性和可维护性。
一、对象结构复杂
当需要创建的对象具有非常复杂的内部结构,包含多个属性或组件,且这些属性或组件的创建和组装过程较为繁琐时,可以使用建造者模式。通过建造者模式,可以将对象的创建过程分解为多个简单的步骤,从而简化对象的创建过程。
二、需要灵活组装对象
在某些情况下,需要根据不同的需求动态地组装一个对象。例如,在软件开发中,可能需要根据不同的配置或参数来创建一个自定义的对象。使用建造者模式,可以方便地通过不同的Builder类来实现对象的灵活组装,而无需修改现有的代码结构。
三、创建过程和使用过程需要分离
在某些应用场景中,对象的创建过程和使用过程需要分离。例如,在某些图形用户界面(GUI)框架中,可能需要先创建一个复杂的窗口对象,然后再将其添加到应用程序中。使用建造者模式,可以将窗口对象的创建过程和使用过程分离,从而简化应用程序的代码结构。
四、需要逐步构建对象
在某些情况下,对象的构建过程需要逐步进行,并且每个步骤都可能需要不同的输入或配置。例如,在构建一个复杂的查询语句时,可能需要先设置查询的字段、表名,然后再设置查询条件、排序方式等。使用建造者模式,可以方便地逐步构建对象,并在每个步骤中进行必要的输入或配置。
4 原型(Prototype)
原型模式是一种非常有用的创建型设计模式,它允许通过复制现有对象来创建新对象,从而简化了对象的创建过程并提高了性能。然而,在使用原型模式时,需要注意浅拷贝和深拷贝的区别以及它们可能带来的问题。
在Python中已经内置了原型模式,直接通过 copy.copy() 和 copy.deepcopy() 实现浅拷贝和深拷贝。