Back to Blogs
设计模式
创建型模式

设计模式一:创建型模式

Soloman
2022-10-05

设计模式一:创建型模式

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() 实现浅拷贝和深拷贝。

5 参考文档