聊聊你不知道的建造者设计模式

2024-06-23 04:08

本文主要是介绍聊聊你不知道的建造者设计模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


距离上次写博客的距离也快一个月了,最近打算跟大家分享一下建造者模式,其实这个模式以前也介绍过,当时只是介绍它在android项目中广泛被应用于创建对象的链式调用,并没有深入的去说明其中的逻辑性,由于这次跟王大哥讨论建造者模式,我还是决定写一篇博客记录一下自己对建造者模式的理解,各位可以提出不同的观点,相互交流

  • 1:Builder 设计模式

    • 1.1:为什么要用建造者模式

    • 1.2:建造者模式的几个角色

    • 1.3:一款软件产品的建造过程

  • 2:变种 Builder 模式:优雅的对象构建方式

    • 2.1:android源码中的Builder 模式:AlertDialog的源码

    • 2.2:我对设计模式的理解

  • 3感悟,偏执到底对一个技术开发和管理者到底是有利还是有弊

1:Builder 设计模式

1.1为什么要用建造者模式

     建造者模式是一种很常见的设计模式,其实在我们android项目中基本上都使用它的变种的模式用于创建对象,这也是谷歌提倡的,暂且不说这个,单说builder顾名思义就是建造的意思,说起建造我们首先想到盖房子,第一步,打桩,第二步,修地基,第三步,砌砖等等,最后封顶,建造者模式跟盖房子本质是一样的的:即流程不变,但每个流程的具体实现是变化的,张二家地基要深一些,墙砌的厚一些,瓦用红色的,李四家由于靠着悬崖房子不能建的太厚,墙身偏薄,等等,一句话具体的流程怎么走不管,但流程不会变化,不会增加也不会减少,这个是非常重要的,我们熟悉的歪楼事件,就是由于先建立楼房后建立停车场,这就是典型的颠倒顺序造成的。
同样的KFC做出来的东西,不论是全国哪家店做出来就都一个味,因为KFC内部有很严格的规定,做巨无霸有做巨无霸的流程,必须严格遵守,这样做出来的东西当然一致了。KFC就是采用了建造者模式!!
说了这么多,那到底什么才是建造者模式呢,官方给出的解释是:
将一个复杂对象的构建和表示分离,同样的构建过程可以创建不同的表示。

如何理解这句话呢,构建和表示分离?何为构建?何为表示?
想弄清楚这些东西,先看下文。

1.2:建造者模式的几个角色

     先理清楚,建造者模式通常包括下面几个角色:

  1. builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建部分,并不涉及具体的对象部件的创建。

  2. ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。(1:完成产品对象的部分构建,2:返回产品实例)

  3. Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。(严格按照流程生产产品)

  4. Product:要创建的复杂对象

建造者模式优势:它将创建复杂对象的创建过程分离,无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。它关注如何一步一步创建一个的复杂对象,不同的具体建造者定义了不同的创建过程,且具体建造者相互独立,增加新的建造者非常方便,无须修改已有代码,系统具有较好的扩展性。

网上经典的建造者模式的例子就是做汉堡
肯德基做汉堡的过程都是有严格的规范的,不管是麦香鸡腿堡还是新奥尔良烤鸡腿堡,他们的制作步骤都是有严格规定,做汉堡的人既不能多做一步,也不能少做一步。对于不同的汉堡来说,虽然每一步加的料所有不同,但做汉堡的步骤都是一样的。因为有了对做汉堡过程的严格控制,因而全国所有的肯德基店做出来的汉堡味道都是一样的。
我没做过汉堡,只会做软件,下面我以我做软件的视角来剖析一下建造者的设计思想。

1.3:一款软件产品的建造过程

下面我们来看一款软件产品的建造过程,
首先创建一个软件开发流程的接口,该接口一般声明两种方法,一种生产各个部分的方法,一种返回复杂产品。
一款软件的实现需要前期编写需求文档,画界面,处理业务逻辑三个部分。

/*** 复杂的产品,考虑到代码的可读性,这里只列举部分的成员属性* 且作为测试成员属性都是String,真实的情况下有些属性是需要自定义的。* Created by zew on 17/7/8.*/
public class Product {private String xuqiu;//需求private String ui;//界面private String luoji;//逻辑public String getXuqiu() {return xuqiu;}public void setXuqiu(String xuqiu) {this.xuqiu = xuqiu;}public String getUi() {return ui;}public void setUi(String ui) {this.ui = ui;}public String getLuoji() {return luoji;}public void setLuoji(String luoji) {this.luoji = luoji;}
}

这里我们抽象一个builder,它定义如何创建复杂对象的各个部件。

/*** 抽象建造者* Created by zew on 17/7/8.*/
public interface  Builder {void buildXuqiu();//文档设计void buildUi();//界面void buildLuoji();//业务逻辑Product getApp();//返回开发的产品
}

然后根据不同的软件我们可以创建不同的具体的创建者,譬如我这里的信用猫创建者用于创建信用猫,信用花创建者用于创建信用花,对于具体的建造者,有三个注意点1:具体完成接口builder来构建或者装配产品部件,2:定义明确所要完成的产品是什么东西,3:提供一个可以获取产品的接口

/*** 信用猫builder具体的实现* Created by zew on 17/7/8.*/
public class CreditCatBuilder implements Builder{Product p;public CreditCatBuilder() {p=new Product();}@Overridepublic void buildXuqiu() {p.setXuqiu("建造信用猫需求文档");}@Overridepublic void buildUi() {p.setUi("建造信用猫画UI");}@Overridepublic void buildLuoji() {p.setLuoji("建造信用猫写逻辑");}//暴露可以获取产品的方法@Overridepublic Product getApp() {return p;}
}
/*** 信用花builder具体的实现* Created by zew on 17/7/8.*/
public class CreditHuaBuilder implements Builder{Product p;public CreditHuaBuilder() {p=new Product();}@Overridepublic void buildXuqiu() {p.setXuqiu("建造信用花设计需求文档");}@Overridepublic void buildUi() {p.setUi("建造信用花画UI");}@Overridepublic void buildLuoji() {p.setLuoji("建造信用花写逻辑");}//暴露可以获取产品的方法@Overridepublic Product getApp() {return p;}
}

指挥者类,该类定一个construct()方法,该方法拥有一个抽象建造者作为参数,该方法内部实现了软件开发的具体流程。

/*** 指挥类,该类定义一个construct方法,该方法拥有一个抽象建造者类型的参数这样可以通过范型把不同的建造者模式都可以传入* Created by zew on 17/7/8.*/
public class AppDirector {//严格按照生产顺序生产产品//完成需求文档builder.buildXuqiu();//完成uibuilder.buildUi();//完成业务逻辑builder.buildLuoji();//返回产品return builder.getApp();}
}

功能测试

  AppDirector appDirector = new AppDirector();//传入信用猫builderProduct p = appDirector.construct(new CreditCatBuilder());xinyongmao.setText(p.getXuqiu()+"==="+p.getUi()+"==="+p.getLuoji());//传入信用花builderProduct p2 = appDirector.construct(new CreditHuaBuilder());xinyonghua.setText(p2.getXuqiu()+"==="+p2.getUi()+"==="+p2.getLuoji());

这里写图片描述

这么两款优秀的app就被生产出来了。

总结:在建造者模式中,只需要创建一个指挥者对象,指挥者类建造者面向接口编程,只需要传入具体的建造者实例类型,指挥者对象就会一步一步构建完成的产品(逐步调用具体的建造者的buildX()方法),相同的构造过程可以完成不同的产品。
在刚刚的app生产实例中,如果想要生产不同的app,只需要更换具体的建造者对象即可,原有的代码无需修改,完全符合“开闭原则”。

写到这大家肯定都暗自高兴原来这就是建造者模式,为自己学习到一技之长而高兴,其实我要说的建造者模式只是开始,下面我们看下android中对建造者的变种。

2变种 Builder 模式:优雅的对象构建方式

        这个链接是我当初写的一个博客,主要是跟大家分享一下,这种通过建造者模式可以优雅的创建对象,这个在java圣经Effective java中被热烈推荐的方式,而且在阅读过android源码的人都知道AlertDialog.builder就是使用这种灵活的方式,大家可以阅读。地址:http://note.youdao.com/share/?id=6060eb9b1063aa6663c13e3516b475b9&type=note

2.1:android源码中的Builder 模式:AlertDialog的源码

下面带着大家看AlertDialog的源码。

final AlertDialog.Builder builder = new AlertDialog.Builder(context);  builder.setCancelable(false);  builder.setIcon(R.drawable.icon);  builder.setTitle("Title");  builder.setView(textEntryView);  builder.setPositiveButton("确认",  new DialogInterface.OnClickListener() {  public void onClick(DialogInterface dialog, int whichButton) {  setTitle(edtInput.getText());  }  });  builder.setNegativeButton("取消",  new DialogInterface.OnClickListener() {  public void onClick(DialogInterface dialog, int whichButton) {  setTitle("");  }  });  builder.show();  }  

通过类名就知道这是一个builder模式,通过builder对象来组装Dialog的各个部分,如,title,message,button,等等,这个在我们信用猫项目中对dialog的二次封装也是沿袭类似的思想,下面看AlertDialog的源码:

由于代码太多,我还是截图说明

这里写图片描述
这里写图片描述
这里写图片描述

由图中可以看出builder类可以设置AlertDialog的title,message,icon,
并且这些参数都被保存在AlertController.AlertParams 的成员变量P参数中,在调用builder类的create方法创建dialog的时候这些被保存的P中的参数会引用到Alert对象中去
下面我们看看图中的P.apply(dialog.mAlert);做了什么。

  public void apply(AlertController dialog) {if (mCustomTitleView != null) {dialog.setCustomTitle(mCustomTitleView);} else {if (mTitle != null) {dialog.setTitle(mTitle);}if (mIcon != null) {dialog.setIcon(mIcon);}if (mIconId != 0) {dialog.setIcon(mIconId);}if (mIconAttrId != 0) {dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));}}if (mMessage != null) {dialog.setMessage(mMessage);}if (mPositiveButtonText != null) {dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,mPositiveButtonListener, null);}if (mNegativeButtonText != null) {dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,mNegativeButtonListener, null);}if (mNeutralButtonText != null) {dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,mNeutralButtonListener, null);}// For a list, the client can either supply an array of items or an// adapter or a cursorif ((mItems != null) || (mCursor != null) || (mAdapter != null)) {createListView(dialog);}if (mView != null) {if (mViewSpacingSpecified) {dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,mViewSpacingBottom);} else {dialog.setView(mView);}} else if (mViewLayoutResId != 0) {dialog.setView(mViewLayoutResId);}/*dialog.setCancelable(mCancelable);dialog.setOnCancelListener(mOnCancelListener);if (mOnKeyListener != null) {dialog.setOnKeyListener(mOnKeyListener);}*/}

在Apply方法中,只是将AlertParams的参数设置到AlertController中,例如将标题的message,title,icon等参数信息设置到内容视图上,我们获取到AlertDialog对象后通过show方法就可以显示对话框.

2.2:我对设计模式的理解

一点点进去最近去你会悄然发现,其实在AlertDialog的builder模式中并没有Director角色的出现,其实在很多场景中,android并没有完全的按照GOF在《设计模式:可服用面向对象软件的基础》一书中描述的经典模式实现来做,而是做了一些修改,这也是Effective java中提倡的一种创建对象的方式,也是我跟王大哥对builder模式的不同解读的很大一个区分点,android对builder模式做了一些修改,使得这个模式更加易用,这里的AlertDialog.builder同时扮演了上文提到的builder,CreditcatBuilder,AppDirector三个角色,简化了builder模式的设计。其实当模块比较稳定,不存在一些变化时没可以在经典的设计模式实现的基础上做一个精简,而不是照搬GOF上的经典的实现,更不要生搬硬套,使程序失去架构之美,真是由于这种灵活的运用设计模式,才让android源码很值得我们去学习。

3:感悟,偏执到底对一个技术开发和管理者到底是有利还是有弊

其实很多人都会这个词持有贬义,隐含着“较真”,“不懂变通”等感情色彩,但是对于做产品,它又显得是一个优点,我对偏执的态度是:我不觉得偏执是每一个技术人员的必须的能力,它是一个加分的能力,但不是一个必备的能力,而且具备这种能力的人一定要有配套的反省能力,我呢,由于性格上就具备这种能力,这对我尝试去做一些看起来很难完成任务的时候,肯定在性格上占了很大一块便宜,但是,从另一个角度来说,如果我是一名管理者,如果我选择的角度不是很明智,这对我个人和团队都会带来危害,总的来说就是如果你有反省能力和自我认知能力足够清醒,没有什么大的偏差的话,偏执对一个管理者是好事,对一个技术人,偏执向来都是利大于弊。。。

这篇关于聊聊你不知道的建造者设计模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1086189

相关文章

十五.各设计模式总结与对比

1.各设计模式总结与对比 1.1.课程目标 1、 简要分析GoF 23种设计模式和设计原则,做整体认知。 2、 剖析Spirng的编程思想,启发思维,为之后深入学习Spring做铺垫。 3、 了解各设计模式之间的关联,解决设计模式混淆的问题。 1.2.内容定位 1、 掌握设计模式的"道" ,而不只是"术" 2、 道可道非常道,滴水石穿非一日之功,做好长期修炼的准备。 3、 不要为了

说一说三大运营商的流量类型,看完就知道该怎么选运营商了!

说一说三大运营商的流量类型,看完就知道该怎么选运营商了?目前三大运营商的流量类型大致分为通用流量和定向流量,比如: 中国电信:通用流量+定向流量 电信推出的套餐通常由通用流量+定向流量所组成,通用流量比较多,一般都在100G以上,而且电信套餐长期套餐较多,大多无合约期,自主激活的卡也是最多的,适合没有通话需求的朋友办理。 中国移动:通用流量+定向流量 移动推出的套餐通常由通用流量+定向

从《深入设计模式》一书中学到的编程智慧

软件设计原则   优秀设计的特征   在开始学习实际的模式前,让我们来看看软件架构的设计过程,了解一下需要达成目标与需要尽量避免的陷阱。 代码复用 无论是开发何种软件产品,成本和时间都最重要的两个维度。较短的开发时间意味着可比竞争对手更早进入市场; 较低的开发成本意味着能够留出更多营销资金,因此能更广泛地覆盖潜在客户。 代码复用是减少开发成本时最常用的方式之一。其意图

[最全]设计模式实战(一)UML六大原则

UML类图 UML类图是学习设计模式的基础,学习设计模式,主要关注六种关系。即:继承、实现、组合、聚合、依赖和关联。 UML类图基本用法 继承关系用空心三角形+实线来表示。实现接口用空心三角形+虚线来表示。eg:大雁是最能飞的,它实现了飞翔接口。 关联关系用实线箭头来表示。当一个类"知道"另一个类时,可以用关联。eg:企鹅需要"知道"气候的变化,需要"了解"气候规律。 聚合关

设计模式学习之中介者模式

我们平时写代码的过程,一个类必然会与其他类产生依赖关系,如果这种依赖关系如网状般错综复杂,那么必然会影响我们的代码逻辑以及执行效率,适当地使用中介者模式可以对这种依赖关系进行解耦使逻辑结构清晰,本篇博客,我们就一起学习中介者模式。 定义及使用场景 定义:中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使它们可以松散耦合。当某些对象之间的作用发生改变时,不会立即影响其

设计模式学习之模版方法模式

模板方法模式是一种基于继承的代码复用的行为型模式;在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。本篇博客我们一起来学习模版方法模式。 定义与UML图 定义 模板方法模式:定义一个操作

Android设计模式学习之Builder模式

Android设计模式学习之观察者模式 建造者模式(Builder Pattern),是创造性模式之一,Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。 模式的使用场景 1.相同的方法,不同的执行顺序,产生不同的事件结果时; 2.多个部件或零件,都可

【设计模式-04】原型模式

【设计模式-04】原型模式 1. 概述2. 结构3. 实现4. 案例5. 使用场景6. 优缺点6.1 原型模式的优点6.2 原型模式的缺点 7. 实现深克隆(深拷贝) 1. 概述 原型模式: 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。 2. 结构 原型模式包含如下角色: 抽象原型类:规定了具体原型对象必须实现的 clone() 方法。

Java中常见的设计模式及应用场景

Java中常见的设计模式及应用场景 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨Java中常见的设计模式及其应用场景,帮助大家更好地理解和应用这些设计模式,提高代码的可维护性和可扩展性。 设计模式概述 设计模式是一种被反复使用的、经过分类的、代码设计中被广泛认可的优秀代码设计经验。它不仅能解决常见的问题,还能

【设计模式之解释器模式 -- C++】

解释器模式 – 语法解析,执行操作 解释器模式是一种设计模式,用于为某个语言定义其语法表示,并提供一个解释器,这个解释器使用该表示来解释语言中的句子。这种模式通常用于开发专门的语言或脚本引擎,可以解析和执行用户定义的指令或表达式。 组成 抽象表达式(Abstract Expression):定义解释操作的接口,这个接口为解释特定的上下文提供了解释(interpret)方法。终结符表达式(Te