建造者模式练习

建造者模式练习——电脑组装、导演套餐与链式调用

按《Python 设计模式——建造者模式》的建议,通过三道练习巩固:① 电脑建造者(分步 set + build,链式调用);② 带导演的套餐(高配机/办公机);③ 带可选步骤的建造者(如显卡)。每步都有完整可运行代码和验证要点。


练习一:电脑建造者(分步组装 + 链式调用)

目的

体会建造者把复杂对象(电脑)的构建拆成多步(set_cpu、set_memory、set_disk),最后 build() 返回产品;每个 set 方法 return self,支持链式调用

要求

  • 定义产品 Computer:有属性 cpu、memory、disk(均为 str,默认 None);实现 str,返回类似 "CPU:xx | 内存:xx | 硬盘:xx"
  • 定义建造者 ComputerBuilder:内部持有一个 Computer 实例;提供 set_cpu(cpu)set_memory(memory)set_disk(disk),每个方法设置对应属性并 return self;提供 build(),返回当前组装好的 Computer,并将内部 Computer 重置为新实例(便于建造者复用)。
  • 客户代码:用链式调用 builder.set_cpu(“i7”).set_memory(“16G”).set_disk(“512G”).build() 得到一台电脑,打印该电脑的 str,验证内容正确;再不设显卡,确认 str 中不出现显卡(或只出现 CPU/内存/硬盘)。

参考答案

class Computer:
    def __init__(self):
        self.cpu = None
        self.memory = None
        self.disk = None

    def __str__(self):
        return f"CPU:{self.cpu} | 内存:{self.memory} | 硬盘:{self.disk}"

class ComputerBuilder:
    def __init__(self):
        self._computer = Computer()

    def set_cpu(self, cpu: str):
        self._computer.cpu = cpu
        return self

    def set_memory(self, memory: str):
        self._computer.memory = memory
        return self

    def set_disk(self, disk: str):
        self._computer.disk = disk
        return self

    def build(self):
        computer = self._computer
        self._computer = Computer()
        return computer

# 使用
builder = ComputerBuilder()
pc = builder.set_cpu("i7").set_memory("16G").set_disk("512G").build()
print(pc)  # CPU:i7 | 内存:16G | 硬盘:512G

验证要点

  • str(pc) 包含 CPU:i7内存:16G硬盘:512G
  • 客户代码是链式调用,没有多行 set;确认每个 set 方法都 return self
  • build() 返回的是新组装好的产品;若再调 builder.build() 不先 set,会得到一台“空”电脑(cpu/memory/disk 均为 None),说明 build 后内部已重置。

练习二:带导演的套餐(高配机 / 办公机)

目的

导演持有建造者,按固定顺序调用建造者的步骤,提供“套餐”方法(如 build_high_end、build_office);调用方只选套餐,不关心先 set_cpu 还是 set_memory。

要求

  • 在练习一的基础上,保留 ComputerComputerBuilder(可增加 set_gpu 以便高配机带显卡)。
  • 定义 Director:构造时接收一个 ComputerBuilder;提供 build_high_end(),内部按顺序 set_cpu(“i9”)、set_memory(“32G”)、set_disk(“1T”)、set_gpu(“RTX4080”)(若建造者有 set_gpu),然后 return builder.build();提供 build_office(),内部 set_cpu(“i5”)、set_memory(“8G”)、set_disk(“256G”),不设显卡,然后 return builder.build()
  • 客户代码:创建 Builder 和 Director,分别调用 director.build_high_end()director.build_office(),验证得到两台不同配置的电脑并打印。

参考答案

class Computer:
    def __init__(self):
        self.cpu = None
        self.memory = None
        self.disk = None
        self.gpu = None

    def __str__(self):
        parts = [f"CPU:{self.cpu}", f"内存:{self.memory}", f"硬盘:{self.disk}"]
        if self.gpu:
            parts.append(f"显卡:{self.gpu}")
        return " | ".join(parts)

class ComputerBuilder:
    def __init__(self):
        self._computer = Computer()

    def set_cpu(self, cpu: str):
        self._computer.cpu = cpu
        return self

    def set_memory(self, memory: str):
        self._computer.memory = memory
        return self

    def set_disk(self, disk: str):
        self._computer.disk = disk
        return self

    def set_gpu(self, gpu: str):
        self._computer.gpu = gpu
        return self

    def build(self):
        computer = self._computer
        self._computer = Computer()
        return computer

class Director:
    def __init__(self, builder: ComputerBuilder):
        self._builder = builder

    def build_high_end(self):
        return (self._builder
            .set_cpu("i9")
            .set_memory("32G")
            .set_disk("1T")
            .set_gpu("RTX4080")
            .build())

    def build_office(self):
        return (self._builder
            .set_cpu("i5")
            .set_memory("8G")
            .set_disk("256G")
            .build())

# 使用
builder = ComputerBuilder()
director = Director(builder)
high_end = director.build_high_end()
office = director.build_office()
print(high_end)  # CPU:i9 | 内存:32G | 硬盘:1T | 显卡:RTX4080
print(office)    # CPU:i5 | 内存:8G | 硬盘:256G

验证要点

  • build_high_end() 得到的电脑包含 i932G1TRTX4080
  • build_office() 得到的电脑包含 i58G256G,且无显卡(或不出现 gpu 字段)。
  • 确认:客户只调 director.build_high_end() / build_office()不关心内部先 set 哪一项;流程封装在导演里。

练习三:带可选步骤的建造者(显卡可选)

目的

建造者中有可选步骤(如 set_gpu):调用就设置,不调用就不设置;产品在 str 或展示时对“未设置”的字段做区分(如不显示或显示“无”)。体会按需调用步骤、避免构造函数参数爆炸。

要求

  • 产品 Computer 有 cpu、memory、disk、gpu;str 中若 gpu 为 None 则不拼接“显卡”部分(或显示“无显卡”)。
  • 建造者 ComputerBuilder 提供 set_cpu、set_memory、set_disk、set_gpu(可选),均 return selfbuild() 返回当前 Computer 并重置内部产品。
  • 客户代码:用同一建造者先造一台带显卡的电脑(set_gpu(“RTX3060”)),再造一台不带显卡的电脑(不调 set_gpu),分别打印两台电脑,验证第一台有显卡、第二台无显卡。

参考答案

class Computer:
    def __init__(self):
        self.cpu = None
        self.memory = None
        self.disk = None
        self.gpu = None

    def __str__(self):
        parts = [f"CPU:{self.cpu}", f"内存:{self.memory}", f"硬盘:{self.disk}"]
        if self.gpu:
            parts.append(f"显卡:{self.gpu}")
        else:
            parts.append("无显卡")
        return " | ".join(parts)

class ComputerBuilder:
    def __init__(self):
        self._computer = Computer()

    def set_cpu(self, cpu: str):
        self._computer.cpu = cpu
        return self

    def set_memory(self, memory: str):
        self._computer.memory = memory
        return self

    def set_disk(self, disk: str):
        self._computer.disk = disk
        return self

    def set_gpu(self, gpu: str):
        self._computer.gpu = gpu
        return self

    def build(self):
        computer = self._computer
        self._computer = Computer()
        return computer

# 使用
builder = ComputerBuilder()
pc1 = builder.set_cpu("i7").set_memory("16G").set_disk("512G").set_gpu("RTX3060").build()
pc2 = builder.set_cpu("i5").set_memory("8G").set_disk("256G").build()  # 不设显卡
print(pc1)  # 含 显卡:RTX3060
print(pc2)  # 含 无显卡

验证要点

  • pc1 的 str 中包含 显卡:RTX3060pc2 的 str 中为 无显卡(或没有“显卡”字段)。
  • 确认:可选步骤通过“调不调 set_gpu”控制,不需要在构造函数里传 gpu=None 等一长串参数。

三步汇总与自检

练习 重点 关键点
建造者 + 链式调用 set 方法 return self;build() 返回产品并可重置内部;链式 set_cpu().set_memory().build()。
导演 + 套餐 导演持有建造者,封装固定流程;客户只调 build_high_end()/build_office()。
可选步骤 需要才调 set_gpu,不调则产品中该字段为 None;按需组装,避免参数爆炸。

自检问题

  1. 建造者的 set 方法为什么要 return self
    为了支持链式调用builder.set_cpu("i7").set_memory("16G").build(),调用方写起来简洁,不必每行单独写一句 set。

  2. build() 之后为什么要重置内部的 _computer?
    若建造者会被多次使用(连续造多台电脑),重置后下一次 set 不会改到已返回的那台电脑;若建造者只用一次,也可以不重置,按需求选择。

  3. 导演直接使用建造者的区别?
    导演封装固定套餐(如高配、办公),客户只选套餐;直接使用建造者可以自由选择调哪些步骤、调几次,更灵活。两者可并存:要套餐用导演,要自定义用建造者。

做完以上三道练习,再对照《建造者模式》文档,对建造者、链式调用、导演和可选步骤会掌握得比较扎实。建议先写电脑建造者与链式调用,再加导演套餐,最后体会可选步骤,这样对建造者模式会掌握得比较扎实。

发表评论