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 是最終實現出來的產品。java

@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

image.png

代碼實現

代碼樣例來自《精通 python 設計模式》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)]

下集預告 模板模式

相關文章
相關標籤/搜索