【设计模式】Builder模式
一、前言
设计模式系列(参考资料:《Android源码设计模式解析与实战》——何红辉、关爱明)
单例模式
Builder模式
原型模式
二、介绍
我们开发中偶尔会遇到一些需要设置10个或以上
大量参数的情况,通常情况有几种处理方式:多构造函数、setter
、Builder模式
。
- 1、多构造函数:这种方式虽然最方便,但也最繁琐,假设有一个方法需要设置
6
个int
型的参数,但多数情况下,这6
个中总有你不想传的参数,这时候就需要约定一个规则:例如传0
,但是传0
又会有一个问题就是传入顺序问题
,传入顺序不同并不会导致程序编译不通过
,却会导致程序运行结果不正确
。所以这并不是一种好方式 - 2、
setter
:
虽然它可以解决多构造带来的传入顺序问题,但是它还是略显繁琐了。所以我们通常只会在JavaBean
中使用setter
- 3、
Builder模式
: - 本文要介绍的一种方式,可以完美解决上述两种方式带来的弊端,让代码更加符合设计模式。参考
Glide
和Retrofit
的使用方式,它们都是通过链式调用
一个一个参数连接起来的
三、定义
Builder
的定义是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
四、使用场景
主要强调两点:调用顺序
、多参数
- 1、相同的方法,不同的执行顺序,需要产生不同的事件结果时
- 2、多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同的时候
- 3、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用时
- 4、当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时
五、UML图
背景:我要建造一个xsic
在这个背景下,Product
是xsic
的抽象,也就是人,ConcreteBuilder
是一个具体Builder
类,继承了抽象Builder
类Builder
,而Director
可以是一个高层客户端模块,也可以是一个为高层客户端模块提供构建功能的中间模块
六、举例
1、抽象产品类,在UML
中没有具体体现
public abstract class Person {protected String mHead;protected int mBody;protected String mArm;protected String mLeg;protected String mHair;public abstract void setHead(String head);public abstract void setBody(int body);public void setmArm(String mArm) {this.mArm = mArm;}public void setmLeg(String mLeg) {this.mLeg = mLeg;}public void setmHair(String mHair) {this.mHair = mHair * mBody;}}
2、具体产品类,对应UML
中的Product
public class Xsic extends Person {public Xsic() {}@Overridepublic void setHead(String head) {mHead = "默认头";}@Overridepublic void setBody(int body) {mBody = 1;}}
3、抽象Builder类,对应UML
中的Builder
public abstract class Builder {public abstract Builder buildHead(String head);public abstract Builder buildBody(int body);public abstract Builder buildArm(String arm);public abstract Builder buildLeg(String leg);public abstract Builder buildHair(String hair);public abstract Person build();}
4、具体Builder类,对应UML
中的ConcreteBuilder
public class XsicBuilder extends Builder {private Xsic xsic = new Xsic();@Overridepublic Builder buildHead(String head) {xsic.setHead(head);return this;}@Overridepublic Builder buildBody(int body) {xsic.setBody(body);return this;}@Overridepublic Builder buildArm(String arm) {xsic.setmArm(arm);return this;}@Overridepublic Builder buildLeg(String leg) {xsic.setmLeg(leg);return this;}@Overridepublic Builder buildHair(String hair) {xsic.setmHair(hair);return this;}@Overridepublic Person build() {return xsic;}}
5、Director
类,可以理解为为高层客户端模块提供构造方式
的类,对应UML
中的Director
,但是通常都不会写这个类,而是直接给高层客户端模块调用。
public class Director {private Builder mBuilder = null;public Director(Builder mBuilder) {this.mBuilder = mBuilder;}public void construct(String head, int body, String arm, String leg, String hair){mBuilder.buildHead(head).buildBody(body).buildArm(arm).buildLeg(leg).buildHair(hair).build();}}
6、客户端,在UML
中没有具体体现
public class Client {private void main(){Builder mBuilder = new XsicBuilder();Person xsic = mBuilder.buildArm("手").buildLeg("脚").buildBody("身体").buildHead("头").build();}}
这个例子里面除了明显的多参数外,还加入了调用顺序产生不同结果:buildBody
调用的先后顺序会影响buildHair
的结果
七、实际使用
实际使用的时候通常情况下并不会有这么多类(除非真的有抽象Builder
的必要,例如要build
一系列类似的事物),通常都会将Director
去掉,毕竟Director
的写法跟setter
没什么差别。因此通常都会只写一个外部类,将Builder
作为这个外部类的静态内部类。
因为使用Glide
经常需要写RequestBuilder
,我们这里以二次封装一个Glide
工具类为例子:
public enum ImagLoader2 {INSTANCE;private static Builder mBuilder = null;public Builder getBuilder(ImageView view){mBuilder = newBuilder(view,view.getContext());return mBuilder;}private Builder newBuilder(ImageView view, Context context){return new Builder(view,context);}public static final class Builder{/* 参数 *///字符串型数据源private String mUrl = "";//资源型数据源private @DrawableRes int mRes = -1;//drawable型数据源private Drawable mDrawable = null;//目标viewprivate ImageView mView;//默认图,只取第一次设置private @DrawableRes int mIntPlaceHolder = -1;private Drawable mDrawablePlaceHolder = null;//错误图,只取第一次设置private @DrawableRes int mIntError = -1;private Drawable mDrawableError = null;//默认图private @DrawableRes int mDefault = -1;//圆角,按left、top、right、bottom顺序,只取第一次设置private int mLTRadius = 0;private int mRTRadius = 0;private int mRBRadius = 0;private int mLBRadius = 0;//全圆角,只取第一次设置private int mRadius = 0;//预览private float mThumnail = 0f;//模糊半径private int mBlurRadius = -1;//是否gif图private boolean mIsGif = false;//是否Bitmapprivate boolean mIsBmp = false;/* 参数 *///是否已经设置数据源,只取第一次设置的值private boolean mIsSourceSet = false;private boolean mIsPlaceHolderSet = false;private boolean mIsErrorSet = false;private RequestOptions mErrorRQ, mPlaceHolderRQ, mRoundCornerRQ, mPartCornerRQ;private RequestManager mGlide;private RequestBuilder mGlideBuilder;private PartCornerTransform2 mPartCornerTransform;private RoundedCorners mRoundedCorners;//四个角的圆角值public enum DIRETION{LEFT_TOP,RIGHT_TOP,RIGHT_BOTTOM,LEFT_BOTTOM}public Builder(ImageView view, Context context) {if (view == null){throw new IllegalArgumentException("目标view必须有效!");}mView = view;mGlide = Glide.with(context);}public Builder setUrl(String url){if (mIsSourceSet) return this;mUrl = url;mIsSourceSet = true;return this;}public Builder setRes(@DrawableRes int res){if (mIsSourceSet) return this;mRes = res;mIsSourceSet = true;return this;}public Builder setDrawable(Drawable drawable){if (mIsSourceSet) return this;mDrawable = drawable;mIsSourceSet = true;return this;}public Builder setDefault(@DrawableRes int res){mDefault = res;return this;}public Builder setPlaceHolder(@DrawableRes int placeHolder){if (mIsPlaceHolderSet) return this;mIntPlaceHolder = placeHolder;mIsPlaceHolderSet = true;return this;}public Builder setPlaceHolder(Drawable placeHolder){if (mIsPlaceHolderSet) return this;mDrawable = placeHolder;mIsPlaceHolderSet = true;return this;}public Builder setError(@DrawableRes int error){if (mIsErrorSet) return this;mIntError = error;mIsErrorSet = true;return this;}public Builder setError(Drawable error){if (mIsErrorSet) return this;mDrawableError = error;mIsErrorSet = true;return this;}public Builder setRadius(int radius){if (radius <= 0){throw new IllegalArgumentException("圆角值必须大于0!圆角值为:"+radius);}mRadius = radius;return this;}/*** 设置各自角的圆角值* @param radius 圆角值* @param diretion 四个角* @return Builder*/public Builder setPartRadius(int radius, DIRETION diretion){if (radius <= 0){throw new IllegalArgumentException("圆角值必须大于0!圆角值为:"+radius);}switch (diretion){case LEFT_TOP:mLTRadius = radius;break;case RIGHT_TOP:mRTRadius = radius;break;case RIGHT_BOTTOM:mRBRadius = radius;break;case LEFT_BOTTOM:mLBRadius = radius;break;}return this;}public Builder setBlurRadius(int blurRadius){if (blurRadius <= 0) {throw new IllegalArgumentException("模糊值必须大于0!模糊值为:"+blurRadius);}mBlurRadius = blurRadius;return this;}public Builder setThumnail(float thumnail){if (thumnail <= 0f){throw new IllegalArgumentException("预览图之于原图的比例不能小于0!当前为:"+thumnail);}mThumnail = thumnail;return this;}public Builder asGif(){mIsGif = true;return this;}public Builder asBitmap(){mIsBmp = true;return this;}public void build(){mGlideBuilder = null;mErrorRQ = new RequestOptions();mPlaceHolderRQ = new RequestOptions();mRoundCornerRQ = new RequestOptions();mPartCornerRQ = new RequestOptions();//数据源if (!mUrl.equals("")){mGlideBuilder = mGlide.load(mUrl);}else if (mRes != -1){mGlideBuilder = mGlide.load(mRes);}else if (mDrawable != null){mGlideBuilder = mGlide.load(mDrawable);}else {if (mDefault != -1){mGlideBuilder = mGlide.load(mDefault);}else {mGlideBuilder = mGlide.load("");}}//错误图if (mIntError != -1){mErrorRQ = mErrorRQ.error(mIntError);mGlideBuilder = mGlideBuilder.apply(mErrorRQ);}else if (mDrawableError != null){mErrorRQ = mErrorRQ.error(mDrawableError);mGlideBuilder = mGlideBuilder.apply(mErrorRQ);}//默认图if (mIntPlaceHolder != -1){mPlaceHolderRQ = mPlaceHolderRQ.placeholder(mIntPlaceHolder);mGlideBuilder = mGlideBuilder.apply(mPlaceHolderRQ);}else if (mDrawablePlaceHolder != null){mPlaceHolderRQ = mPlaceHolderRQ.placeholder(mDrawablePlaceHolder);mGlideBuilder = mGlideBuilder.apply(mPlaceHolderRQ);}//圆角if (mRadius != 0){mRoundedCorners = new RoundedCorners(mRadius);mRoundCornerRQ = RequestOptions.bitmapTransform(mRoundedCorners);mGlideBuilder = mGlideBuilder.apply(mRoundCornerRQ);}//各自圆角if (mLTRadius != 0 || mRTRadius != 0 || mRBRadius != 0 || mLBRadius != 0){mPartCornerTransform = new PartCornerTransform2(mLTRadius,mRTRadius,mRBRadius,mLBRadius);mPartCornerRQ = RequestOptions.bitmapTransform(mPartCornerTransform);mGlideBuilder = mGlideBuilder.apply(mPartCornerRQ);}//预览if (mThumnail > 0f){mGlideBuilder = mGlideBuilder.thumbnail(mThumnail);}//是否gifif (mIsGif){mGlideBuilder = mGlide.asGif();}//是否bmpif (mIsBmp){mGlideBuilder = mGlide.asBitmap();}mGlideBuilder.into(mView);}}
}
八、总结
Builder
模式如果真的要吹毛求疵找出缺点的话,那就是会比普通setter
或者多构造函数
方式多了一个Builder对象的创建
,这对于对性能要求苛刻的应用来说可能不是一个最好的选择,但是对于大多数应用来说,Builder
的优势还是显而易见的。
ps:这篇和《Effective Java》第二章 第2条:遇到多个构造器参数时要考虑使用Builder内容一样,因此我就直接copy
,为什么不直接跳过?因为缺了一篇的话会像少了一块拼图一样让我很难受…
【设计模式】Builder模式相关推荐
- 设计模式-Builder模式
目录 一个例子(做汤) 人工做汤 机器做汤(使用Builder模式) 优缺点 优点 缺点 Builder模式属于创建型模式. 它是将一个复杂对象的构建过程隐藏起来,让使用者只关系自己要生成什么样的对象 ...
- 设计模式--builder 模式
设计模式–builder 模式 说明 @author JellyfishMIX - github / blog.jellyfishmix.com LICENSE GPL-2.0 本文默认已经知道了 b ...
- Java设计模式——Builder模式
前言 之前写Android程序的时候,经常会用到Dialog(对话框)这个控件.我们在使用Dialog,比如AlertDialog的时候就用到了这里要说明的Builder模式.现在我们来看一下这样的一 ...
- 设计模式 —— Builder 模式
文章目录 1 Builder 模式的核心思想 2 第一种 Builder 模式 3 第一种模式的困惑 4 第二种 Builder 模式 5 第二种 Builder 模式见解 6 参考 1 Builde ...
- Java 常用设计模式 -- Builder模式
Builder模式是在Java中最流行的模式之一.它很简单,有助于保持对象不可变,并且可以使用Project Lombok的@Builder或Immutables等工具生成,仅举几例. 模式的流畅变体 ...
- java的设计模式 - Builder模式
Builder 模式的目的? 构造对象的方式过于复杂,不如将之抽离出来.比如,构造器参数过多 这样说也有点抽象,举个例子吧. 举个例子 比如 非常热门的消息队列RabbitMQ 的 AMQP.Basi ...
- GOF 设计模式 builder模式笔记
Builder(生成器) 意图:将一个复杂对象的构建与它的表示相分离,使得同样的构建过程可以创建不同的表示. 在以下情况使用Builder模式 当创建复杂的对象应该独立于该对象的组成部分以及它们的装配 ...
- Java设计模式-Builder模式
Builder模式是将一个复杂对象的创建和表示分离,使同样的创建过程可以创建不同的表示.它属于建造类模式. 一般来说,如果一个对象的创建比较复杂,超出了构造方法所能包含的范围,就可以使用工厂模式和Bu ...
- 设计模式 builder模式
也是属于对象创建类模式.使用的一般情况是,需要完成一个复杂的对象创建工作,工作通常由各个部分的子对象用一定的算法构成,比如step1,step2,step3,步骤内部常常发生剧烈的变化,但是组合在一起 ...
- 设计模式-Builder模式(构建者模式)
目录 构建者模式是什么? 为什么要用构建者模式? 构建者模式是什么? 简单来说,就是用于组装具有复杂结构的实例的模式. 什么意思呢?先来看个例子,比如现在有一个类TextBuilder用来创建一个文本 ...
最新文章
- 必读!53个Python经典面试题详解
- HDU2032(杨辉三角)
- 如何提升科研能力?以下这点最重要!
- 非洲儿童(南阳oj1036)(馋)
- CocoaPosd使用详解
- css清除浮动的原理
- 三十岁以前不必在乎的29件事
- java 调用python_Java平台如何调用Python平台?
- 基于Java+SpringMvc+vue+element实现高效学生社团平台管理
- TensorFlow-RNN循环神经网络 Example 2:文本情感分析
- 40元老年机摇身一变成“华为”手机:半年卖了7000多台?
- java类加载机制ClassLoad
- JAVA分布式架构的演变及解决方案
- 【文章汇总】嵌入式Linux公众号
- wifi连接过程抓包
- 【毕业求职季】-听说你想去大厂看学妹,带你看看字节广告运营岗面试长啥样?
- Beetlsql自学笔记
- Android Exif 解析
- Unity RenderTexture实现 刮彩票、橡皮擦、擦除效果(3D物体)
- APP Launch 优化
热门文章
- WINDOWS文件夹下的应用程序
- 关心一下开放源码的许可证
- i7运行linux虚拟机会卡吗,i7 7200 linux 虚拟机
- 服务器主板北桥芯片组有哪些,主板上北桥芯片组都负责管理哪些硬件?
- win10命令行模式无法切换输入法
- (转)AndroidManifest 清单文件合并时出现 【quires a placeholder substitution but no value for is provided.】问题
- 联想st510开卡软件_无力吐槽的一单联想ST510固态硬盘数据恢复
- 优化算法—人工蜂群算法(ABC)
- Javaweb和微信小程序项目部署阿里云服务器总结(上)
- 手机有时触摸失灵解决方法