Neil 啃设计模式(0x04)建造者模式

建造者模式(Builder Pattern)

定义

Separate the construction of a complex object from its representation so that the same construction process can create different representations.(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。)
《设计模式之禅》

UML 示例

以创建不同口味 pizza 的过程为例,Margarita Pizza 和 CreamyBacon Pizza,而 Waiter 作为一个 Director, Pizza 是最终实现出来的产品。

@startuml
Interface Builder
class MargaritaBuilder
class CreamyBaconBuilder
class Pizza
note left: 每个builder会创建自己的Pizza样式,而Pizza定义的内容很少
class Waiter
note left: waiter负责按照一定的执行顺序依次执行具体的builder中的步骤,产生一个pizza
Waiter #-- Builder
Builder <|-- MargaritaBuilder
Builder <|-- CreamyBaconBuilder
MargaritaBuilder ..> Pizza
CreamyBaconBuilder ..> Pizza

@enduml

Neil 啃设计模式(0x04)建造者模式_第1张图片

代码实现

代码样例来自《精通 python 设计模式》

# coding: utf-8

from enum import Enum
import time

# 定义执行过程和Pizza口味的打印信息
PizzaProgress = Enum('PizzaProgress', 'queued preparation baking ready')
PizzaDough = Enum('PizzaDough', 'thin thick')
PizzaSauce = Enum('PizzaSauce', 'tomato creme_fraiche')
PizzaTopping = Enum('PizzaTopping', 'mozzarella double_mozzarella bacon ham mushrooms red_onion oregano')
STEP_DELAY = 3          # 考虑是示例,单位为秒


class Pizza:
    # 要创建的产品,pizza,包含几个部分dough、sauce、topping
    def __init__(self, name):
        self.name = name
        self.dough = None
        self.sauce = None
        self.topping = []

    def __str__(self):
        return self.name

    # 此段代码也可以在builder中实现,目的是为了展示不仅仅是可以定义Pizza属性,也可以定义一些方法,
    # 来让builder调用
    def prepare_dough(self, dough):
        self.dough = dough
        print('preparing the {} dough of your {}...'.format(self.dough.name, self))
        time.sleep(STEP_DELAY)
        print('done with the {} dough'.format(self.dough.name))


class MargaritaBuilder:

    def __init__(self):
        # builder类创建的margatita pizza对象
        # 此处能够表现出产品的创建由builder类创建
        self.pizza = Pizza('margarita')
        self.progress = PizzaProgress.queued
        self.baking_time = 5        # 考虑是示例,单位为秒

    # 如下的过程都是builder类抽象出的统一接口,所有的builder都有这些接口,由于python与java不同,可以不用提前定义抽象类来继承
    def prepare_dough(self):
        self.progress = PizzaProgress.preparation
        self.pizza.prepare_dough(PizzaDough.thin)

    def add_sauce(self):
        print('adding the tomato sauce to your margarita...')
        self.pizza.sauce = PizzaSauce.tomato
        time.sleep(STEP_DELAY)
        print('done with the tomato sauce')

    def add_topping(self):
        print('adding the topping (double mozzarella, oregano) to your margarita')
        # 此处实在topping的0位置添加了一个[]list
        self.pizza.topping.append([i for i in
                                   (PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
        time.sleep(STEP_DELAY)
        print('done with the topping (double mozzarrella, oregano)')

    def bake(self):
        self.progress = PizzaProgress.baking
        print('baking your margarita for {} seconds'.format(self.baking_time))
        time.sleep(self.baking_time)
        self.progress = PizzaProgress.ready
        print('your margarita is ready')


class CreamyBaconBuilder:

    def __init__(self):
        self.pizza = Pizza('creamy bacon')
        self.progress = PizzaProgress.queued
        self.baking_time = 7        # 考虑是示例,单位为秒

    def prepare_dough(self):
        self.progress = PizzaProgress.preparation
        self.pizza.prepare_dough(PizzaDough.thick)

    def add_sauce(self):
        print('adding the crème fraîche sauce to your creamy bacon')
        self.pizza.sauce = PizzaSauce.creme_fraiche
        time.sleep(STEP_DELAY)
        print('done with the crème fraîche sauce')

    def add_topping(self):
        print('adding the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano) to your creamy bacon')
        self.pizza.topping.append([t for t in
                                   (PizzaTopping.mozzarella, PizzaTopping.bacon,
                                    PizzaTopping.ham, PizzaTopping.mushrooms,
                                    PizzaTopping.red_onion, PizzaTopping.oregano)])
        time.sleep(STEP_DELAY)
        print('done with the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano)')

    def bake(self):
        self.progress = PizzaProgress.baking
        print('baking your creamy bacon for {} seconds'.format(self.baking_time))
        time.sleep(self.baking_time)
        self.progress = PizzaProgress.ready
        print('your creamy bacon is ready')


# waiter作为一个director定义了执行过程
class Waiter:

    def __init__(self):
        self.builder = None

    def construct_pizza(self, builder):
        self.builder = builder
        # step by step一步一步的执行pizza的制作过程
        [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_style = input('What pizza would you like, [m]argarita or [c]reamy bacon? ')
        builder = builders[pizza_style]() # 根据用户输入产生builder
        valid_input = True
    except KeyError as err:
        print('Sorry, only margarita (key m) and creamy bacon (key c) are available')
        return (False, None)
    return (True, builder)


def main():
    builders = dict(m=MargaritaBuilder, c=CreamyBaconBuilder)
    valid_input = False
    while not valid_input:
        valid_input, builder = validate_style(builders)
    print()
    waiter = Waiter()
    waiter.construct_pizza(builder)
    pizza = waiter.pizza
    print()
    print('Enjoy your {}!'.format(pizza))

if __name__ == '__main__':
    main()

应用场景

  • 建造者模式个人理解是客户化,定制化装配过程的时候需要使用到的。
  • 建造者模式与工厂模式的区别就在于工厂模式的产品是固定规格的,写死的,非定制化的。就好比是自己买的品牌型号固定的电脑,还是自己组装一台电脑一样。前者是所有参数固定了,而后者参数由客户自己决定。
  • 怎样理解将构建与表示分离? 构建就是创建的过程,一个产品应该是一个复杂的产品,由多个组成部分构成,而产品的构建过程有一个统一的流程可以被抽象或者定义,而具体的实现是由每个 builder 类实例定制化的。
  • 表示就是指一个产品的各个属性,而构建就是如何赋值这些属性,这些属性该赋予什么指等等是由 builder 来构建的。
  • 所以这样的好处就是创建一个 product,product 类只是提供简单的属性定义,而具体赋予什么值,是由每个具体的 builder 定制的。比如上面例子中的 Pizza,Pizza 的属性或者不成部分就只这些,而不同的 builder 就会不予不同的值。最终是由 builder 创建出的 Pizza
  • 而 director 是根据客户需要,调用不同 builder 的一个执行者,是将调用的动作进行封装,由于 builder 会提供多个执行步骤,而对于这些执行步骤的执行是不希望被用户看到的,所以进行了封装。有了 director 类

知识点

  • 学习 enum 的使用
  • 编程中调用一系列函数的简化流程,表明 step by step 的流程,打包一个流程的方式
[step() for step in (builder.prepare_dough,
                             builder.add_sauce, builder.add_topping, builder.bake)]

下集预告 模板模式

你可能感兴趣的:(python,设计模式,uml)