Python 设计模式 - 建造者模式

精通Python设计模式第二版 第 2 章 建造者模式 学习笔记

建造者模式

假设我们要创建一个由对各部分组成的独享,而且创建过程需要一步一步地完成。只有创建了所有的部分,该对象才是完成的。这种需求可以使用建造者模式帮助我们。建造者模式将复杂对象的构建与其表示分离。通过将构建与表示分离,相同的构建可用于创建几个不同的表示

假设我们想创建一个HTML页面生成器。HTML页面的基础结构(构建部分)大同小异:以开始,以结束,在HTML部分中有元素,在head部分中有原色,以此类推。但是页面的表示可能不同。每个页面都有自己的标题、头部和不同的内容。此外,页面通常按步骤构建:一个函数添加标题,一个函数添加主头部,一个函数添加脚注,等等。只有在页面的整个结构完成之后,才能使用一个最终的渲染函数将其呈现给客户端。我们可以进一步拓展HTML生成器,使其能够生成完全不同的HTML页面。一个页面可能包含表格,一个可能包含图像库,一个页面可能包含联系人表单,等等

两个参与者

  • 建造者(builder):负责创建复杂独享的各个部分的组件。
  • 指挥者(director):使用建造者实例控制构建过程的组件。

定制个人计算机问题

假设我们想买一台新计算机,计算机的配置我们自己挑选。我们可以使用建造者模式解决这个问题,在这个方案里,我们就是指挥者,向制造商(builder)发出指令

1. 定义Computer类

class Computer():
    """
    Computer :

    Attributes:

    """

    def __init__(self, serial_number):
        self.serial = serial_number
        self.memory = None  # 单位为GB
        self.hdd = None  # 单位为GB
        self.gpu = None

    def __str__(self):
        info = (f'Memory: {self.memory}GB',
                f'Hard Disk: {self.hdd}GB',
                f'Graphics Card: {self.gpu}')
        return '\n'.join(info)

2. 定义ComputerBuilder类

class Computer():
    """
    Computer :

    Attributes:

    """

    def __init__(self, serial_number):
        self.serial = serial_number
        self.memory = None  # 单位为GB
        self.hdd = None  # 单位为GB
        self.gpu = None

    def __str__(self):
        info = (f'Memory: {self.memory}GB',
                f'Hard Disk: {self.hdd}GB',
                f'Graphics Card: {self.gpu}')
        return '\n'.join(info)

3. 定义HardwareEngineer类

class HardwareEngineer():
    """
    HardwareEngineer :

    Attributes:

    """

    def __init__(self):
        self.builder = None

    def construct_computer(self, memory, hdd, gpu):
        self.builder = ComputerBuilder()
        (self.builder.configure_memory(memory),
         self.builder.configure_hdd(hdd),
         self.builder.configure_gpu(gpu))

    @property
    def computer(self):
        return self.builder.computer


4. 以main()函数结束,并从命令行调用方法

def main():
    engineer = HardwareEngineer()
    engineer.construct_computer(hdd=500,
                                memory=8,
                                gpu='GeForce GTX 650 Ti')
    computer = engineer.computer
    print(computer)


if __name__ == '__main__':
    main()

获得打印如下

Memory: 8GB
Hard Disk: 500GB
Graphics Card: GeForce GTX 650 Ti

完整代码如下


class Computer():
    """
    Computer :

    Attributes:

    """

    def __init__(self, serial_number):
        self.serial = serial_number
        self.memory = None  # 单位为GB
        self.hdd = None  # 单位为GB
        self.gpu = None

    def __str__(self):
        info = (f'Memory: {self.memory}GB',
                f'Hard Disk: {self.hdd}GB',
                f'Graphics Card: {self.gpu}')
        return '\n'.join(info)


class ComputerBuilder():
    """
    ComputerBuilder :

    Attributes:

    """

    def __init__(self):
        self.computer = Computer('AG23385193')

    def configure_memory(self, amount):
        self.computer.memory = amount

    def configure_hdd(self, amount):
        self.computer.hdd = amount

    def configure_gpu(self, gpu_model):
        self.computer.gpu = gpu_model


class HardwareEngineer():
    """
    HardwareEngineer :

    Attributes:

    """

    def __init__(self):
        self.builder = None

    def construct_computer(self, memory, hdd, gpu):
        self.builder = ComputerBuilder()
        (self.builder.configure_memory(memory),
         self.builder.configure_hdd(hdd),
         self.builder.configure_gpu(gpu))

    @property
    def computer(self):
        return self.builder.computer


def main():
    engineer = HardwareEngineer()
    engineer.construct_computer(hdd=500,
                                memory=8,
                                gpu='GeForce GTX 650 Ti')
    computer = engineer.computer
    print(computer)


if __name__ == '__main__':
    main()

这个方案中,我们引入了建造者CompuerBuilder、指挥者HardwareEngineer,并逐步构建了一台计算机,它现在支持不同的配置memory,hdd,gpu

定制披萨问题

现在让我们看看如果使用建造者设计模式来制作比萨订购程序。披萨的例子特别又去,因为披萨的制作不走应该遵循特定的顺序。加调味汁之前,首先要准备面团。加配料之前,首先要加调味汁。只有调味汁和配料都放在面团上后,你才能开始烤披萨。此外,每个披萨通常需要不同的烘烤时间,这取决于面团的厚度和所用的配料

1. 声明一些Enum

使用这些枚举来记录制作披萨的状态以及披萨的一些相关属性

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  # 单位为秒

2. 定义Pizza类

class Pizza():
    """
    Pizza :

    Attributes:

    """

    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'preparing the {self.dough.name} dough of your {self}...')
        time.sleep(STEP_DELAY)
        print(f'done with the {self.dough.name} dough')

3. 定义Builder类

有两个建造者:一个用于创建玛格丽特披萨,另一个用于创建奶油培根披萨。每个建造者创建一个Pizza实例,并包含遵循比萨制作过程的方法:prepare_dough()、add_sauce() 等。

class MargaritaBuilder():
    """
    MargaritaBuilder :

    Attributes:

    """

    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):
        topping_desc = 'double mozzarella, oregano'
        topping_items = (PizzaTopping.double_mozzarella, PizzaTopping.oregano)
        print(f'adding the topping ({topping_desc}) to your margarita')
        self.pizza.topping.append([t for t in topping_items])
        time.sleep(STEP_DELAY)
        print(f'done with the topping ({topping_desc})')

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


class CreamyBaconBuilder():
    """
    CreamyBaconBuilder :

    Attributes:

    """

    def __int__(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):
        topping_desc = 'mozzarella, bacon, ham, mushrooms, red onion, oregano'
        topping_items = (PizzaTopping.mozzarella,
                         PizzaTopping.bacon,
                         PizzaTopping.ham,
                         PizzaTopping.mushrooms,
                         PizzaTopping.red_onion,
                         PizzaTopping.oregano)
        print(f'adding the topping ({topping_desc}) to your creamy bacon')
        self.pizza.topping.append([t for t in topping_items])
        time.sleep(STEP_DELAY)
        print(f'done with the topping ({topping_desc})')

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

4. 定义Waiter类

在这个例子中 指挥者是服务员。Waiter类的核心是construct_pizza()方法,它接收一个建造者作为参数,并以正确的顺序执行所有比萨准备。

class Waiter():
    """
    Waiter :

    Attributes:

    """

    def __init__(self):
        self.builder = None

    def construct_pizza(self, builder):
        self.builder = builder
        steps = (builder.prepare_dough,
				 builder.add_sauce,
                 builder.add_topping,
                 builder.bake)
        [step() for step in steps]
    @property
    def pizza(self):
        return self.builder.pizza

5. 处理订单需求

validate_style() 确保用户提供有效的输入

def validate_style(builders):
    try:
        input_msg = 'What pizza would you like, [m]argarita or [c]reamy bacon? \n'
        pizza_style = input(input_msg)
        builder = builders[pizza_style]()
        valid_input = True
    except KeyError:
        error_msg = 'Sorry, only margarita (key m) and creamy bacon (key c) are available'
        print(error_msg)
        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(f'Enjoy your {pizza}!')

if __name__ == '__main__':
    main()

获得打印如下

What pizza would you like, [m]argarita or [c]reamy bacon? 
m

preparing the thin dough of your margarita...
done with the thin dough
adding the tomato sauce to your margarita
done with the tomato sauce
adding the topping (double mozzarella, oregano) to your margarita
done with the topping (double mozzarella, oregano)
baking your margarita for 5 seconds
your margarita is ready

Enjoy your margarita!

完整代码如下


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():
    """
    Pizza :

    Attributes:

    """

    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'preparing the {self.dough.name} dough of your {self}...')
        time.sleep(STEP_DELAY)
        print(f'done with the {self.dough.name} dough')


class MargaritaBuilder():
    """
    MargaritaBuilder :

    Attributes:

    """

    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):
        topping_desc = 'double mozzarella, oregano'
        topping_items = (PizzaTopping.double_mozzarella, PizzaTopping.oregano)
        print(f'adding the topping ({topping_desc}) to your margarita')
        self.pizza.topping.append([t for t in topping_items])
        time.sleep(STEP_DELAY)
        print(f'done with the topping ({topping_desc})')

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


class CreamyBaconBuilder():
    """
    CreamyBaconBuilder :

    Attributes:

    """

    def __int__(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):
        topping_desc = 'mozzarella, bacon, ham, mushrooms, red onion, oregano'
        topping_items = (PizzaTopping.mozzarella,
                         PizzaTopping.bacon,
                         PizzaTopping.ham,
                         PizzaTopping.mushrooms,
                         PizzaTopping.red_onion,
                         PizzaTopping.oregano)
        print(f'adding the topping ({topping_desc}) to your creamy bacon')
        self.pizza.topping.append([t for t in topping_items])
        time.sleep(STEP_DELAY)
        print(f'done with the topping ({topping_desc})')

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


class Waiter():
    """
    Waiter :

    Attributes:

    """

    def __init__(self):
        self.builder = None

    def construct_pizza(self, builder):
        self.builder = builder
        steps = (builder.prepare_dough,
				 builder.add_sauce,
                 builder.add_topping,
                 builder.bake)
        [step() for step in steps]
    @property
    def pizza(self):
        return self.builder.pizza


def validate_style(builders):
    try:
        input_msg = 'What pizza would you like, [m]argarita or [c]reamy bacon? \n'
        pizza_style = input(input_msg)
        builder = builders[pizza_style]()
        valid_input = True
    except KeyError:
        error_msg = 'Sorry, only margarita (key m) and creamy bacon (key c) are available'
        print(error_msg)
        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(f'Enjoy your {pizza}!')

if __name__ == '__main__':
    main()

流畅创建者

简略版建造者模型 解决披萨订单问题


class Pizza():
    """
    Pizza :

    Attributes:

    """
    def __init__(self,builder):
        self.garlic = builder.garlic
        self.extra_cheese = builder.extra_cheese

    def __str__(self):
        garlic = 'yes' if self.garlic else 'no'
        cheese = 'yes' if self.extra_cheese else 'no'
        info = (f'Garlic: {garlic}',f'Extra cheese: {cheese}')
        return '\n'.join(info)

    class PizzaBuilder():
        """
        PizzaBuilder :

        Attributes:

        """
        def __init__(self):
            self.extra_cheese = False
            self.garlic = False

        def add_garlic(self):
            self.garlic = True
            return self

        def add_cheese(self):
            self.extra_cheese = True
            return self

        def build(self):
            return Pizza(self)

if __name__ == '__main__':
    pizza = Pizza.PizzaBuilder().add_cheese().add_garlic().build()
    print(pizza)

获得打印如下

Garlic: yes
Extra cheese: yes

你可能感兴趣的:(Python,学习,python,设计模式,建造者模式)