zoukankan      html  css  js  c++  java
  • (转自精通Python设计模式)Python设计模式之创建型模式——2.建造者模式

      建造者模式将一个复杂对象的构造过程与其表现分离,这样,同一个构造过程可用于创建多个不同的表现。

      我们来看个实际的例子,假设我们想要创建一个HMTL页面生成器,HTML页面的基本结构(构造组件)通常是一样的:以<html>开始</html>结束,在HTML部分中有<head>和</head>元素,在head部分中又有<title>和</title>元素,等等;但页面在表现上可以不同。每个页面有自己的页面标题、文本标题以及不同的body内容。此外,页面通常是经过多个步骤创建完成的:有一个函数添加页面标题,另一个添加主文本标题,还有一个添加页脚,等等。仅当一个页面的结构全部完成后,才能使用一个最终的渲染函数将该页面展示在客户端。我们甚至可以更进一步扩展这个HTML生成器,让它可以生成一些完全不同的HTML页面。一个页面可能包含表格,另一个页面可能包含图像库,还有一个页面包含联系表单,等等。

      HTML页面生成问题可以使用建造者模式来解决。该模式中,有两个参与者:建造者(builder)和指挥者(director) 。建造者负责创建复杂对象的各个组成部分。在HTML例子中,这些组成部分是页面标题、文本标题、内容主体及页脚。指挥者使用一个建造者实例控制建造的过程。对于HTML示例,这是指调用建造者的函数设置页面标题、文本标题等。使用不同的建造者实例让我们可以创建不同的HTML页面,而无需变更指挥者的代码。

      案例

      让我们来看看如何使用建造者设计模式实现一个比萨订购的应用。比萨的例子特别有意思,因为准备好一个比萨需经过多步操作,且这些操作要遵从特定顺序。要添加调味料,你得先准备生面团。要添加配料,你得先添加调味料。并且只有当生面团上放了调味料和配料之后才能开始烤比萨饼。此外,每个比萨通常要求的烘焙时间都不一样,依赖于生面团的厚度和使用的配料。

      完整示例代码(builder.py)如下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Date    : 2018/7/15 9:59
    # @Author  : Yaheng Wang (m13262578991@163.com)
    # @Link    : http://www.wy2160640.github.io
    # @Version : 1.0
    
    from enum import Enum
    import time
    
    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:
        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('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):
            self.pizza = Pizza('margarita')
            self.progress = PizzaProgress.queued
            self.baking_time = 5
    
        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')
            self.pizza.topping.append([i for i in (PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
            time.sleep(STEP_DELAY)
            print('done with the topping (double mozzarella, 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 creme fraiche sauce to your creamy bacon')
            self.pizza.sauce = PizzaSauce.creme_fraiche
            time.sleep(STEP_DELAY)
            print('done with the creme fraiche 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, mushroom, 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')
    
    
    class Waiter:
        def __init__(self):
            self.builder = None
    
        def construct_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_style = input('What pizza would you like, [m]argarita or [c]reamy bacon?')
            builder = builders[pizza_style]()
            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()
    

       小结

      在以下几种情况下,与工厂模式相比,建造者模式是更好的选择。

        1.想要创建一个复杂对象(对象由多个部分构成,且对象的创建要经过多个不同的步骤也许还需要遵从特定的顺序)

        2.要求一个对象能有不同的表现,并希望将对象的构造与表现解耦

        3.想要在某个时间点创建对象,但在稍后的时间点再访问

  • 相关阅读:
    oc之数组反序输出示例
    OC--有这么一个 整数 123456789,如何将这个整数的每一位数,从末位开始依次放入数组中,并遍历 倒序输出字符串
    oc--截取字符串(从网址中截取用户名和密码)
    iOS 第七期考核题(字符串以及字典的使用 数组内容转换成字符串)
    iOS 第六期考核题(字典的使用)
    iOS 第五期考核题(字典与数组嵌套,字典的排序/删除)
    Linux服务启动报错日志分析
    新手Linux命令-1
    新手Linux命令-2
    计划任务服务
  • 原文地址:https://www.cnblogs.com/yahengwang/p/9343447.html
Copyright © 2011-2022 走看看