编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。
思考总结
思考问题
没有生成器模式的情况下在构建不同形式的复杂对象时的问题:
- 如果为每种可能的对象都创建一个子类,这可能会导致程序变得过于复杂。
- 拥有大量输入参数的构造函数也有缺陷:这些参数也不是每次都要全部用上的。通常情况下,绝大部分的参数都没有使用,这使得对于构造函数的调用十分不简洁。
什么是建造者(生成器)模式
生成器是一种创建型设计模式,使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。
建造者(生成器)模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
含义:
-
该模式会将对象构造过程划分为一组步骤, 重点在于你无需调用所有步骤,而只需调用创建特定对象配置所需的那些步骤即可。在这种情况下,你可以创建多个不同的生成器,用不同方式实现一组相同的创建步骤。
-
你可以进一步将用于创建产品的一系列生成器步骤调用抽取成为单独的主管类。主管类可定义创建步骤的执行顺序,而生成器则提供这些步骤的实现。
-
主管类完全隐藏了产品构造细节。客户端只需要将一个生成器与主管类关联。
注意:
-
具体生成器需要自行提供获取结果的方法。原因有两点
- 我们无法在主管类和具体产品类不发生耦合的情况下,在主管类中提供获取结果对象的方法。因此,我们只能通过负责制造过程的生成器来获取结果对象。
- 不同类型的生成器可能会创建不遵循相同接口的、完全不同的产品。所以也就无法在生成器接口中声明这些方法(至少在静态类型的编程语言中是这样的)。
-
由于客户端可以直接控制生成器,所以严格意义上来说,主管类并不是必需的。
主要解决:
- 主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:
- 一些基本部件不会变,而其组合经常变化的时候。只有当产品较为复杂且需要详细配置时,使用生成器模式才有意义。
实现方法:
- 清晰地定义通用步骤, 确保它们可以制造所有形式的产品。
- 在基本生成器接口中声明这些步骤。
- 为每个形式的产品创建具体生成器类,并实现其构造步骤。
- 不要忘记实现获取构造结果对象的方法。你不能在生成器接口中声明该方法,因为不同生成器构造的产品可能没有公共接口,因此你就不知道该方法返回的对象类型。如果所有产品都位于单一类层次中,你就可以安全地在基本接口中添加获取生成对象的方法。
- 考虑创建主管类。它可以使用同一生成器对象来封装多种构造产品的方式。
- 客户端代码会同时创建生成器和主管对象。通常有两种方式。
- 只调用一次主管对象构造方法,调用的时候传入生成器实例。
- 客户端将生成器对象直接传递给主管类的构造方法。
- 只有在所有产品都遵循相同接口的情况下,构造结果可以直接通过主管类获取。否则,客户端应当通过生成器获取构造结果。
应用实例:
- 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
- JAVA 中的 StringBuilder。
优点:
-
可避免“重叠构造函数(telescoping constructor)”的出现。
-
你可以分步创建对象,暂缓创建步骤或递归运行创建步骤。
-
生成不同形式的产品时,你可以复用相同的制造代码。
-
单一职责原则。你可以将复杂构造代码从产品的业务逻辑中分离出来。
-
建造者独立,易扩展。
-
便于控制细节风险。
缺点:
- 产品必须有共同点,范围有限制。
- 如内部变化复杂,会有很多的建造类。
与其他模式的区别:
-
在许多设计工作的初期都会使用工厂方法(较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂、原型或生成器(更灵活但更加复杂)。
-
生成器与其他创建型模式的不同之处它让你能创建不遵循相同接口的产品。
-
与工厂模式的区别是,建造者模式更加关注与零件装配的顺序,分步生成复杂对象。
-
抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品,生成器则允许你在获取产品前执行一些额外构造步骤(比如复制对象再重置对象然后返回复制对象引用)。
-
你可以在创建复杂组合树时使用生成器,因为这可使其构造步骤以递归的方式运行。
-
你可以结合使用生成器和桥接模式:主管类负责抽象工作, 各种不同的生成器负责实现工作。
-
抽象工厂、生成器和原型都可以用单例来实现。
参考资料
- 《Go语言核心编程》李文塔
- 《Go语言高级编程》柴树彬、曹春辉
- 《大话设计模式》程杰
- 《深入设计模式》亚历山大·什韦茨
- 菜鸟教程