Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx

上传人:牧羊曲112 文档编号:4883218 上传时间:2023-05-21 格式:DOCX 页数:8 大小:196.20KB
返回 下载 相关 举报
Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx_第1页
第1页 / 共8页
Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx_第2页
第2页 / 共8页
Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx_第3页
第3页 / 共8页
Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx_第4页
第4页 / 共8页
Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx_第5页
第5页 / 共8页
亲,该文档总共8页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx》由会员分享,可在线阅读,更多相关《Builder模式的误区 将复杂对象的构建进行封装,就是Builder模式了吗.docx(8页珍藏版)》请在三一办公上搜索。

1、Builder模式的误区:将复杂对象的构建进行封装,就是Builder模式了吗?最近重读GOF的设计模式,读到Builder模式的时候,发现还是不能领悟;网上搜了下 其他人的解释,发现很多人都用错了 Builder模式,结构形似Builder,实际上却更像Template 或者Factory Method,或者四不像,并没有体现出Builder模式的思想和威力;通过对比学习, 也逐渐加深了我对Builder模式的认识,于是就有了这篇文章。0. GOF - Builder 模式下面是GOF对Builder模式的部分阐述,先列出来,用于与后文中的错误案例进行对比。文 字很精辟,不易理解;但若真正理

2、解了,会发现这些文字对已经将Builder模式的精髓描述完了。(1) 意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。(2) 适用性:当同时满足以下情况的时候可以使用Builder模式a. 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式;b. 当构造过程必须允许构造的对象有不同的表示;(3) 结构:Director|ConstructBuilderGuilder Part 0builder-Bu iIdPart 0;CoticretuBuildorProductProductfor a. 1 objects n structureBuildor

3、Pcirt () KciResult ()happyhippy. cnblogs. corn留意图中红色注释部分,尤其是循环,灰常灰常重要,呵呵,这也是很多人在使用Builder模式 时所忽略的部分。(想想,为什么不是一句builder-BuildPart()就够了,为什么要有这个循环 呢?)(4)协作:1(5)相关模式:Composite通常是用Builder生成的。先不解释这些文字,先看两个例子,看看使用Builder模式的误区。这两个例子皆来自书上(对 不起,我不是恶意挑刺,实在是两位大哥太出名了-_-,还请见谅)。1.组合不同数量的现有部件,需要定义新的Builder子类吗?先看- 一

4、个例子:Builder模式应用实践(此图从原文中copy来的)这个例子中,设备(Equipment)是一个复杂对象,由一个Machine和一个(或多个)输入端口 (InputPort)或者输出端口 (OutputPort)组成;此设计中定义了一个LCDFactory(充当导向器 Director的角色)、一个设备生成器(EQPBuilder),及三个 ConcreteBuilder: InputEQPBuilder 生成的 Equipment = 1 个 Machine + 1 个 InputPort ; OutputEQPBuilder 生成的 Equipment = 1 个 Machine

5、 + 1 个 OutputPort ; IOPutEQPBuilder 生成的 Equipment = 1 个 Machine + 1 个 InputPort + 1 个 OutputPort ;此设计对复杂对象Equipment的创建过程进行了封装,在应对需求变化上,作者的解释是: “例如要求创建的Equipment包含一个Machine对象,一个Input类型的Port,两个Output 类型的Port,那么我们可以在不修改原有程序集的前提下,新定义一个IO2PutEQPBuilder类, 并继承自抽象类EQPBuilder”。也就是说,每当要给设备增加端口的时候,我们就要创建新的Buil

6、der子类。我们把这个需 求扩大化,如果要创建一个Equipment,其可能包含0M(M=0)个InputPort、0N(N=0) 个OutputPort,这样可能组合出(M+1) * (N + 1)个Equipment,因此我们就需要创建(M + 1) * (N + 1)个ConcreteBuilder,这会带来Builder子类数量的急剧膨胀;其本质上是通过继承来达到 构建不同的Equipment o这与Builder模式的思想是相违背的,结合Builder模式的结构图来看, 导向器(Diretor)是调用BuildPart()方法,来将部件(Part)组合到目标Product中的;如果

7、只是组合不同数量的现有部件,则不用定义新的ConcreteBuildero因此,虽然这个类图几乎形似Builder模式,但却并不是Builder模式的应用。2. Builder模式就是创建复杂对象的模板吗?一些Builder模式的“应用”,感觉更像是一个创建复杂对象的模板;而对Builder模式与 Template Method模式的区分,则认为Builder模式是侧重于创建复杂对象,而Template Mehod 则侧重于对象的行为。在我看来,这个观点是错误的。如果把Builder当作一个创建复杂对象的模板,则基本上可 以断定,Builder模式被误用了。Builder模式的类图结构中,装配

8、复杂对象的组成部分,是用 BuilderPart()方法来定义,如果我们把这个装配操作视为一个操作行为,是不是意味着这种情况 下的Builder模式就是一个Template Method 了呢?我认为答案是否定的。Builder模式与 Template Method模式有着天壤之别,二者毫不相干;前者偏重于通过聚合来组装对象,后 者偏重于通过继承来重写对象的行为。下面再来看下,大话设计模式中的例子:该应用中,要求画一个小人,要有头、身体、 两手、两脚。给出的类图结构如下所示:PersonBui Id&rPersmThihBuilderPersooFatBuildarBuildHeM 0 +Bu

9、iIdBody 0 + BuildAmeft 0 +BuildAmi?ight 0 + Bui】d_egl 莉上 0BuildLegRieht ()+ Bmldilead 0+BLildBody 0汗 BuildArmLeft 0BuiIdArmRight 0+ Bui IdLegLeft 0IdLegRight 0中回ildK蛔0 +BuildBody 0 +BuildATTnI/eft 0 BuildArnfiight () +Buildl闻点ft 0 + ildLegRight 0(此图从该书中copy来的)在这个例子中,原作者要求小人不能缺胳膊少腿;我猜测,其中也隐含着小人不能有三头六

10、 臂。作者认为“这里构造小人的过程是稳定的,都需要头身手脚,而具体构造的细节是不同的, 有胖有瘦有高有矮”。在应对高矮胖瘦的需求变化时,只需要增加新的PersonBuilder子类就行 了。如果需要细化细节,“比如人的五官、手的上臂、前臂和手掌,大腿小腿”,“这些细节是每 个具体的小人都需要构建的”,则需要将这些接口加到Builder接口中;Builder模式中的Builder 接口必须要“足够普遍,以便各种类型的具体建造者构造”。虽然这个类图几乎神似Builder模式,但细细斟酌,却也有不妥的地方:(1)需求变化时,人可高可矮可胖可瘦,所以可以该设计中,就可为高矮胖瘦分别创建 Concret

11、eBuilder,但假如需求继续变化,要实现高胖、矮胖、高瘦、矮瘦呢,是否需要继续扩 展Builder?在我的理解中,高矮胖瘦体现的是“人的特征”;游戏中小人的特征可能非常多(方脑 壳、圆脑壳、O型腿、八字脚、咸猪手),为创建每个不同特征的小人,都配置一个Builder,可 能不太现实。(2)此示例中,原作者认为“构造小人的过程是稳定的”;偏重于去说明,面对“细节”(即组成人 的部件:头、身、手、脚)的差异,需要创建新的ConcreteBuilder;这就容易给人造成一种错觉: 造小人的过程是一个类似于一个模板,构造不同的小人时,需要继承该模板、重写差异来实现新 小人的生成。(3) Build

12、er模式构造出来的不同种类的Product,这些Product的组成部分(part)相互不能进行 替换或组合,否者将会带来ConcreteBuilder数量的急剧膨胀。这一点在后面再来说。Builder模式的核心是“聚合”,这个例子中,并没有把Builder模式的思想体现出来。3. Builder模式示例为了避免构造新的示例,便于比较和理解,我直接在上面两个例子的基础上进行修改:3.1 改进版的 EQPBuilder与之前的EQPBuilder的区别在哪儿呢?(1) . InputPort、OutputPort、Machine等,是复杂对象Equipment的组成部分,这些部件的装 配方式在A

13、ddXXOOPort、BuildMachine等方法中定义;而如何根据这些部件来创建复杂 Equiment的算法,在导向器类LCDFactory中定义;这就使得“创建复杂对象的算法独立于该对 象的组成部分以及他们的装配方式”。(2) . AddXXOOPort、BuildXXOOMachine 等接口,封装了部件(Port、machine)与产品(Equipment)的装配方式,Add操作可能比较复杂,其可能封装了初始化Port设备、执行插拔、 焊接等操作;LCDFactory作为创建设备导向器,如果其构造设备的过程中,要增加多个Port, 则只需要多次复用Builder的Add操作即可。因此

14、,如果只是组合不同数量的现有部件,本质 上只是“创建复杂对象的算法”被改变了;因为我们只用调整算法部分LCDFactory就可以了,而 不用去创建新的ConcreteBuilder;(3).当且仅当新的部件(SuperXXOO)需要加入到系统时,才需要去创建新的ConcreteBuilder; 如果要创建SuperEquipment,我们只需要将SuperEQPBuilder的示例传递给LCDFactory就足 够了,这里复用了原有的构造Equipment的算法。3.2 改进版的 PersonBuilder与之前PersonBuilder的差别:(1) .奥特曼是机器人,变形金刚是汽车人,统统

15、可以抽象出来当人,有头有身体有胳膊有腿(暂 时不考虑汽车人变形的情况)。BuildPart(Head/Body/Arm/Leg)封装了创建部件、并装配人身上 的操作;这个Build操作供导向器复用。PersonDirector定义了创建人的多种算法,不同算法 调用BuildPart()的顺序和次数不同,可以生成出具有1头1身2臂2腿的常规人,也可以创建 独臂刀客,还可以创建三头六臂的超人等。(2) .构造小人的算法是灵活多变的,该算法在PersonDirector中定义;至于如何变,可以用其 他设计模式来实现,这里不与讨论,这里侧重的是Builder模式的应用。只要我们将不同的 Concret

16、eBuilder传递给同一 PersonDirector,就可以得到不同的人(人类、机器人、汽车人), 从而复用了创建Person的算法,达到同样的构件过程可以创建不同的表示。3.3 特例:StringBuilderI| Client3StringBuilderchar* arrayi-Append (in str : string) +ToStringOhappyhippycnblogscom在这个Builder模式的实现中,Client同时充当了 Director的角色;StringBuilder同时充当了 Builder接口和ConcreteBuilder。这是一个最简化的Builder

17、模式的实现。/Client同时充当了 Director的角色StringBuilder builder = new StringBuilder(); builder.Append(happyhippy);builder.Append(.cnblogs);builder.Append(.com);/返回 string 对象: builder.ToString();4. Builder模式的核心思想将一个“复杂对象的构建算法”与它的“部件及组装方式分离,使得构件算法和组装方式可以 独立应对变化;复用同样的构建算法可以创建不同的表示,不同的构建过程可以复用相同的部 件组装方式。抽象的Builder类

18、,为导向者可能要求创建的每一个构件(Part)定义一个操作(接口)。这些操 作缺省情况下什么都不做。一个ConcreteBuilder类对它所感兴趣的构建重定义这些操作。每个 ConcreteBuilder包含了创建和装配一个特定产品的所有代码(注意:ConcreteBuilder只是 提供了使用部件装配产品的操作接口,但不提供具体的装配算法,装配算法在导向器Director 中定义)。这些代码只需要写一次;然后不同的Director可以复用它,以在相同部件集合的基 础上构建不同的Product o回过头再来看,类图结构中对Director的注释,为什么不是一句builder-BuildPar

19、t()就够了, 为什么要有这个循环呢? BuildPart方法封装了创建Part、并组装到Product中的操作,循环调 用调用多次时,可以反复复用BuildPart操作,让目标Product聚合多个Part。再进一步:如果 Part中可以聚合多个Part,然后递归下去,可以组合成一颗树型结构,这就是Composite 了; 在来理解相关模式中的这句话:“Composite通常是用Builder生成的”,就很容易理解了。另外,需要指出的一点。单纯的Builder模式中,“不同Product类型”的组成部件之间, 不能进行组合或替换。譬如上面的两个示例中:组成普通Equipment的普通Inpu

20、tPort、OutputPort、Machine,不允许与组成 SuperEquipment 的 SuperInputPort、SuperOutputPort、 SuperMachine进行组合创建新的Equipment;人的头身臂腿,与奥特曼的头身臂腿,或者汽车 人的头身臂腿,三者之间的部件不能兼容或替换。这一点GOF在DP中并没有说明,但是在他 们给出的两个例子中,充分体现了这一点:RTF的三个转换器,ASCIIConvert只负责组合 ASCIICharactar,TeXConverter 之负责组合自身格式的部件(Charactor、FontChange、 Paragraph),Tex

21、tWidgetConverter 同理;因此不可能出现由 TextWidget 格式的 Charactor 和 TeX格式的Paragraph组合而成的Text。GOF的另一个Builder模式的应用示例是 StandardMazeBuilder与CountingMazeBuilder; GOF在介绍创建型模式时,前后多次用到Wall/BombedWall、Room/RoomWithABomb,为什么这里 GOF 偏偏不用BombedMazeBuilder,而别出心裁搞出个CountingMazeBuilder;他们很巧妙地回避了部件替 换问题。假如允许“不同Product类型”的组成部件之间进行组合或替换,譬如我们允许将奥特曼 的头与变形金刚的头进行互换,或者允许将机器人的身体替换的人的身体来构建出钢铁侠,或者 使用其他组合来构建金刚狼,我们该怎么办呢?这个问题已经超出了 Builder模式的范畴,先留 着。相关模式DP : Abstract Factory与Builder相似,因为它可以创建复杂对象。主要的区别是 Builder模式着重于一步步构造一个复杂对象,而Abstract Factory着重于多个系列的产品对象 (简单的或复杂的)。Builder是最后一步返回产品,而Abstract Factory是立即返回产品。Composite通常是用Builder生成的。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号