23种设计模式漫画版系列—生成器模式(一)

简介: 23种设计模式漫画版系列—生成器模式

本文介绍了   生成器设计模式,UML  类图关系画uml实用工具。博主提取了实际工作中的干货,耐心的读下去吧!


关注公众号:全栈芬达,回复:亿图图示,获取uml工具激活版。

1意图

生成器模式是一种创建型设计模式 使你能够分步骤创建复杂对象 该模式允许你使用相同的创建代码生成不同类型和形式的对象


2问题

假设有这样一个复杂对象 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作 这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中 甚至还有更糟糕的情况 那就是这些代码散落在客户端代码的多个位置

如果为每种可能的对象都创建一个子类 这可能会导致程序变得过于复杂

例如 我们来思考如何创建一个 房屋House对象 建造一栋简单的房屋 首先你需要建造四面墙和地板 安装房门和一套窗户 然后再建造一个屋顶 但是如果你想要一栋更宽敞更明亮的房屋 还要有院子和其他设施 例如暖气 排水和供电设备 那又该怎么办呢

最简单的方法是扩展 房屋基类 然后创建一系列涵盖所有参数组合的子类 但最终你将面对相当数量的子类 任何新增的参数 例如门廊类型 都会让这个层次结构更加复杂

另一种方法则无需生成子类 你可以在 房屋基类中创建一个包括所有可能参数的超级构造函数 并用它来控制房屋对象 这种方法确实可以避免生成子类 但它却会造成另外一个问题

拥有大量输入参数的构造函数也有缺陷 这些参数也不是每次都要全部用上的


通常情况下 绝大部分的参数都没有使用 这使得对于构造函数的调用十分不简洁 例如 只有很少的房子有游泳池 因此与游泳池相关的参数十之八九是毫无用处的


3解决方案

生成器模式建议将对象构造代码从产品类中抽取出来 并将其放在一个名为生成器的独立对象中

生成器模式让你能够分步骤创建复杂对象 生成器不允许其他对象访问正在创建中的产品


该模式会将对象构造过程划分为一组步骤 比如 build­Walls创建墙壁build­Door创建房门等 每次创建对象时 你都需要通过生成器对象执行一系列步骤 重点在于你无需调用所有步骤 而只需调用创建特定对象配置所需的那些步骤即可

当你需要创建不同形式的产品时 其中的一些构造步骤可能需要不同的实现 例如 木屋的房门可能需要使用木头制造 而城堡的房门则必须使用石头制造

在这种情况下 你可以创建多个不同的生成器 用不同方式实现一组相同的创建步骤 然后你就可以在创建过程中使用这些生成器 例如按顺序调用多个构造步骤 来生成不同类型的对象

不同生成器以不同方式执行相同的任务


例如 假设第一个建造者使用木头和玻璃制造房屋 第二个建造者使用石头和钢铁 而第三个建造者使用黄金和钻石 在调用同一组步骤后 第一个建造者会给你一栋普通房屋 第二个会给你一座小城堡 而第三个则会给你一座宫殿 但是 只有在调用构造步骤的客户端代码可以通过通用接口与建造者进行交互时 这样的调用才能返回需要的房屋

主管

你可以进一步将用于创建产品的一系列生成器步骤调用抽取成为单独的主管 主管类可定义创建步骤的执行顺序 而生成器则提供这些步骤的实现


严格来说 你的程序中并不一定需要主管类 客户端代码可直接以特定顺序调用创建步骤 不过 主管类中非常适合放入各种例行构造流程 以便在程序中反复使用

此外 对于客户端代码来说 主管类完全隐藏了产品构造细节 客户端只需要将一个生成器与主管类关联 然后使用主管类来构造产品 就能从生成器处获得构造结果了

4

生成器模式结构


5伪代码

下面关于生成器模式的例子演示了你可以如何复用相同的对象构造代码来生成不同类型的产品——例如汽车 Car——及其相应的使用手册 Manual

分步骤制造汽车并制作对应型号用户使用手册的示例



汽车是一个复杂对象 有数百种不同的制造方法 我们没有在 汽车类中塞入一个巨型构造函数 而是将汽车组装代码抽取到单独的汽车生成器类中 该类中有一组方法可用来配置汽车的各种部件

如果客户端代码需要组装一辆与众不同 精心调教的汽车 它可以直接调用生成器 或者 客户端可以将组装工作委托给主管类 因为主管类知道如何使用生成器制造最受欢迎的几种型号汽车

你或许会感到吃惊 但确实每辆汽车都需要一本使用手册 说真的 谁会去读它们呢?) 使用手册会介绍汽车的每一项功能 因此不同型号的汽车 其使用手册内容也不一样 因此 你可以复用现有流程来制造实际的汽车及其对应的手册 当然 编写手册和制造汽车不是一回事 所以我们需要另外一个生成器对象来专门编写使用手册 该类与其制造汽车的兄弟类都实现了相同的制造方法 但是其功能不是制造汽车部件 而是描述每个部件 将这些生成器传递给相同的主管对象 我们就能够生成一辆汽车或是一本使用手册了

最后一个部分是获取结果对象 尽管金属汽车和纸质手册存在关联 但它们却是完全不同的东西 我们无法在主管类和具体产品类不发生耦合的情况下 在主管类中提供获取结果对象的方法 因此 我们只能通过负责制造过程的生成器来获取结果对象

classCaris


classManualis

// 用户使用手册应该根据汽车配置进行编制,并介绍汽车的所有功能。
// 生成器接口声明了创建产品对象不同部件的方法。
interface Builder is
    method reset()
    method setSeats(...)
    method setEngine(...)
    method setTripComputer(...)
    method setGPS(...)
// 具体生成器类将遵循生成器接口并提供生成步骤的具体实现。你的程序中可能会
// 有多个以不同方式实现的生成器变体。
class CarBuilder implements Builder is
    private field car:Car
    // 一个新的生成器实例必须包含一个在后续组装过程中使用的空产品对象。
    constructor CarBuilder() is
        this.reset()
    // reset(重置)方法可清除正在生成的对象。
    method reset() is
        this.car = new Car()
    // 所有生成步骤都会与同一个产品实例进行交互。
    method setSeats(...) is
        // 设置汽车座位的数量。
    method setEngine(...) is
        // 安装指定的引擎。
    method setTripComputer(...) is
        // 安装行车电脑。
    method setGPS(...) is
        // 安装全球定位系统。
    method getProduct():Car is
        product = this.car
        this.reset()
        return product
// 生成器与其他创建型模式的不同之处在于:它让你能创建不遵循相同接口的产品。
class CarManualBuilder implements Builder is
    private field manual:Manual
    constructor CarManualBuilder() is
        this.reset()
    method reset() is
        this.manual = new Manual()
    method setSeats(...) is
        // 添加关于汽车座椅功能的文档。
    method setEngine(...) is
        // 添加关于引擎的介绍。
    method setTripComputer(...) is
        // 添加关于行车电脑的介绍。
    method setGPS(...) is
        // 添加关于 GPS 的介绍。
    method getProduct():Manual is
        // 返回使用手册并重置生成器。
// 主管只负责按照特定顺序执行生成步骤。
class Director is
    private field builder:Builder
    method setBuilder(builder:Builder)
        this.builder = builder
    // 主管可使用同样的生成步骤创建多个产品变体。
    method constructSportsCar(builder: Builder) is
        builder.reset()
        builder.setSeats(2)
        builder.setEngine(new SportEngine())
        builder.setTripComputer(true)
        builder.setGPS(true)
    method constructSUV(builder: Builder) is
        // ...
// 客户端代码会创建生成器对象并将其传递给主管,然后执行构造过程。最终结果
// 将需要从生成器对象中获取。
class Application is
    method makeCar() is
        director = new Director()
        CarBuilder builder = new CarBuilder()
        director.constructSportsCar(builder)
        Car car = builder.getProduct()
        CarManualBuilder builder = new CarManualBuilder()
        director.constructSportsCar(builder)
        // 最终产品通常需要从生成器对象中获取,因为主管不知晓具体生成器和
        // 产品的存在,也不会对其产生依赖。
        Manual manual = builder.getProduct()
相关文章
|
9月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
285 16
|
9月前
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
275 0
|
9月前
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
299 0
|
9月前
|
设计模式 安全 Java
并发设计模式实战系列(12):不变模式(Immutable Object)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十二章,废话不多说直接开始~
226 0
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
825 11
|
9月前
|
设计模式 算法 Java
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
9月前
|
设计模式 Prometheus 监控
并发设计模式实战系列(20):扇出/扇入模式(Fan-Out/Fan-In)(完结篇)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第二十章,废话不多说直接开始~
311 0
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
278 40