Builder模式是代码编写过程中经常会用到的一类设计模式。最近重读了《设计模式》builder章节,同时也读了其他人对于builder模式的理解和应用,在此记录我自己对Builder模式的一些理解。
一、GOF - Builder模式
下面是GOF对Builder模式的部分阐述。
1.意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2.适用性
在以下情况使用Builder模式
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
3.组成
Builder
为创建一个Product对象的各个部件指定抽象接口。
ConcreteBuilder
实现Builder的接口以构造和装配该产品的各个部件。
定义并明确它所创建的表示。
提供一个获取产品的接口。
Director
构造一个使用Builder接口的对象。
Product
表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程 。
包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
4.结构
屏幕快照 2018-03-12 上午11.47.49.png5.协作
屏幕快照 2018-03-12 下午1.45.56.png二、个人理解
1.builder模式要点
- 用于分步骤构建一个复杂的对象。builder提供几种固定的步骤接口和获取最终对象接口,这些步骤接口的调用次数、调用顺序由Director决定,最终通过获取最终对象接口得到最终产品。
- 支持多种构建算法。不同构件算法可以创建出不同的表现。Builder模式封装了构建算法,调用者无需知道具体的构建算法细节。
- 拥有稳定的部件类型及部件装配方式。builder接受的组件类型和组件装配方式稳定不变,构建算法和它的组件互不影响。
2.builder模式优点
- 将一个“复杂对象的构建算法”与它的“部件及组装方式”分离,使得构建算法可以独立应对变化;
- 不同构件算法可以复用相同的部件和部件组装方式;
- 不同构件算法,可以生成不同表现的对象;
- “部件类型“及其”组装方式“相对稳定,不随着构建算法改变而改变。
理解builder模式重点在两个词,“创建对象”和“一变一不变”。
“创建对象”指明builder模式的最终目的为创建对象,是大前提。“一变一不变”即构建算法多变,但是部件和部件装配方式稳定不变,既可以用于描述builder模式使用场景,也是builder模式的特点。
三、常见错误理解
1.不同部件表现/不同部件数目导致不同“表现”
builder模式中的“表现”为宏观表现,依赖于对象的构建算法并且跟随对象的构建算法改变而改变。在使用同一种构建算法的情况下,不同的“部件表现”并不影响到对象的宏观表现,不同的部件数目也不影响到对象的宏观表现。
场景描述:以鱼、青菜为材料做一道菜,厨师可用“煎炒烹炸”多种烹饪方式来做这道菜,可以得到“水煮鱼”、“红烧鱼”、“炸鱼条”等多种菜肴
在这个场景中:
部件为“鱼”、“青菜”
构建算法为“煎炒烹炸”不同的烹饪方式
生成对象为“菜肴”不同的烹饪方式对应着不同的菜肴表现
不同的“鱼”(部件表现),青花鱼、鲤鱼等,和鱼或青菜的数量(部件数目),并不影响到最终的菜肴类型
2.builder模式目的只在于构建复杂对象
很多网络上/书上对builder模式的解析,都过分强调了“构建复杂对象”,没有充分理解到“多变的构造算法和不变的组件”这一必要条件。容易写出形似Builder但是内在为Template或者Factory的模式。
初学Android代码时接触到Notification.Builder/AnimatorSet.Builder,用于创建复杂的“Notification”和“AnimatorSet”对象,误以为这就是Builder模式,在深入了解builder模式之后才明白这并不是builder模式,是创建复杂对象的一种写法。这种写法也是一种较为流行的创建方法,模式上属于Factory模式写法上兼有builder模式的特点,此种创建方式有以下优点:
- 将复杂对象的创建和表示分离
- 对外部调用者隐藏了具体的创建过程
- 封装复杂对象的创建过程,可对创建过程做统一的处理(例如设置默认属性/计数)
四、例子解析
1.《Builder模式应用实践》
这个例子中,设备(Equipment)是一个复杂对象,由一个Machine和一个(或多个)输入端口(InputPort)或者输出端口(OutputPort)组成;此设计中定义了一个LCDFactory(充当导向器[Director]的角色)、一个设备生成器(EQPBuilder),及三个ConcreteBuilder:
InputEQPBuilder生成的Equipment = 1个Machine + 1个InputPort;
OutputEQPBuilder生成的Equipment = 1个Machine + 1个OutputPort;
IOPutEQPBuilder生成的Equipment = 1个Machine + 1 个InputPort + 1个OutputPort;
例如要求创建的Equipment包含一个Machine对象,一个Input类型的Port,两个Output类型的Port,那么我们可以在不修改原有程序集的前提下,新定义一个IO2PutEQPBuilder类,并继承自抽象类EQPBuilder
在这个例子中,Equipment为最终生成产品,Machine、InputPort、OutputPort为组件,在这个例子中并没有体现出多种“构建算法”对于“产品表现”的影响,组件的数量并不影响最终产品的表现。更适合使用Factory模式。
2.《大话设计模式》
《大话设计模式》中的例子:该应用中,要求画一个小人,要有头、身体 、两手、两脚。给出的类图结构如下所示(书中配图)
image_thumb.png
在这个例子中,Person为最终生成产品,Head、Body、Arm、Leg为组件,不同的构建算法用来构建“PersonThin”和“PersonFat”,并且组件和装配方式都是稳定不变的,乍一看是符合builder模式的,但是仔细分析会有以下问题:
- Thin/Fat只是部件的表现形式,并不是各个部分的装配算法,应该改为
buildHead(Head head)
,Head再扩展为FatHead
、ThinHead
,部件的不同表现应该通过不同部件来表现 - 组件和装配方式稳定不变,但是认真来看例子中的组件并不具备灵活性,例如不可多次调用
BuildHead()
,更适合使用Template模式来实现。
五、相关模式
1.Abstract Factory模式
相似点:
- 目的相同,用于创建复杂对象
不同点:
- Builder模式着重构造一个复杂对象;Abstract Factory模式着重于创建多个系列的产品对象.
- Builder模式着重于一步步构建对象,并在最后一步返回;Abstract Factory模式里对象是立即返回的。
2.Composite模式
相似点:
- 都是由多个部分按照一定逻辑组成一个复杂对象
不同点:
- 目的不同,Builder模式在于创建对象;Composite模式在于描述对象间的关系
3.Strategy模式
相似点:
- 支持不同算法
- 不同算法生成不一样的结果
区别点:
- 目的不同,Builder模式在于创建对象;Strategy模式在于描述行为
网友评论