一、前言

《Effective Java》读书笔记系列
第二章 第1条:创建和销毁对象
第二章 第2条:遇到多个构造器参数时要考虑使用Builder

二、介绍

我们开发中偶尔会遇到一些需要设置10个或以上大量参数的情况,通常情况有几种处理方式:多构造函数、setterBuilder模式

  • 1、多构造函数:这种方式虽然最方便,但也最繁琐,假设有一个方法需要设置6int型的参数,但多数情况下,这6个中总有你不想传的参数,这时候就需要约定一个规则:例如传0,但是传0又会有一个问题就是传入顺序问题,传入顺序不同并不会导致程序编译不通过,却会导致程序运行结果不正确。所以这并不是一种好方式
  • 2、setter
    虽然它可以解决多构造带来的传入顺序问题,但是它还是略显繁琐了。所以我们通常只会在JavaBean中使用setter
  • 3、Builder模式
  • 本文要介绍的一种方式,可以完美解决上述两种方式带来的弊端,让代码更加符合设计模式。参考GlideRetrofit的使用方式,它们都是通过链式调用一个一个参数连接起来的

三、定义

Builder的定义是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

四、使用场景

主要强调两点:调用顺序多参数

  • 1、相同的方法,不同的执行顺序,需要产生不同的事件结果时
  • 2、多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同的时候
  • 3、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用时
  • 4、当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时

四、UML图

背景:我要建造一个xsic
在这个背景下,Productxsic的抽象,也就是人,ConcreteBuilder是一个具体Builder类,继承了抽象BuilderBuilder,而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的优势还是显而易见的。

【读书笔记】《Effective Java》第二章 第2条:遇到多个构造器参数时要考虑使用Builder相关推荐

  1. Effective Java 创建和销毁对象 2.遇到多个构造器参数时要考虑用构建器

    静态工厂跟构造器都有一个共同的局限性:不能很好的扩展到大量的参数. 例: package com.example.yancy.yancy; /**  * Created by yancy on 201 ...

  2. [Effective Java]第二章 创建和销毁对象

    第一章      前言 略... 第二章      创建和销毁对象 1.            考虑用静态工厂方法代替构造器 创建对象方法:一是最常用的公有构造器,二是静态工厂方法.下面是一个Bool ...

  3. mysql数据库权威指南_MySQL_MySQL权威指南读书笔记(三),第二章:MYSQL数据库里面的数 - phpStudy...

    MySQL权威指南读书笔记(三) 第二章:MYSQL数据库里面的数据 用想用好MYSQL,就必须透彻理解MYSQL是如何看待和处理数据的.本章主要讨论了两个问题:一是SQL所能处理的数据值的类型:二是 ...

  4. C语言基础教程读书笔记2(第二章常量、变量、类型转换)

    第二章常量.变量.类型转换<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

  5. [读书笔记]Effective Java 第四章

    使类和成员的可访问性最小化 规则很简单:尽可能地使每个类或者成员不被外界访问.实例域(非final)决不能是公有的.当需要暴露出不可变的实例时通常会把这个实例做成不可变或者是把这个实例变成私有,同时提 ...

  6. Think in Java第四版 读书笔记10 第16章 数组

    Think in Java第四版 读书笔记10 第16章 数组 数组和容器很像 但他们有一些差别 16.1 数组为什么特殊 数组与容器的区别主要在效率和存储类型 效率:数组是简单的线性序列 使得数组的 ...

  7. Think in Java第四版 读书笔记9第15章 泛型

    Think in Java第四版 读书笔记9第15章 泛型 泛型:适用于很多很多的类型 与其他语言相比 Java的泛型可能有许多局限 但是它还是有很多优点的. 本章介绍java泛型的局限和优势以及ja ...

  8. 黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第四章 使用SCAPY掌控网络(2)Scapy实现ARP缓存投毒

    黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第四章 使用SCAPY掌控网络(2)Scapy实现ARP缓存投毒 文章目录 黑帽python第二版(Bl ...

  9. 黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第五章 WEB黑客(3)暴力破解目录与文件位置

    黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第五章 WEB黑客(3)暴力破解目录与文件位置 文章目录 黑帽python第二版(Black Hat P ...

最新文章

  1. android 调用java类_Android中在WebView里实现Javascript调用Java类的方法
  2. python nose(二)
  3. Intel VT学习笔记(九)—— EPT应用示例
  4. PHP版本号--phpversion(),PHP_VERSION,PHP_VERSION_ID
  5. rm: cannot remove directory `test': Permission denied
  6. python代码颜色搭配_Python IDLE代码配色及语法高亮
  7. 重庆“扫黄打非”部门打掉一网络传黄团伙,查获淫秽视频11万余部
  8. 一个用于消息队列的并发式php进程管理程序-守护
  9. arcgis更改图层坐标系_以图层的形式添加 x,y 坐标数据
  10. OmniConverter: Mac上的最简单好用的免费全能音视频转换器
  11. MySQL的图形化安装
  12. bootstrap设计登录页面_微服务和VUE(2) 搭建前端登录界面
  13. lpddr3 阻抗_LPDDRx的总结
  14. 自动发射子弹c语言,C语言实现简单飞机大战
  15. 码农的奋斗之路 富爸爸穷爸爸系列--提高你的财商 读后感
  16. MAVEN 修改为阿里数据源
  17. JS笔记:检测客户端(引擎、浏览器、平台、操作系统、移动设备)
  18. python自动化测试登录_自动化测试(二)如何用python写一个用户登陆功能
  19. 台式机安装windowsXP和ubuntu双系统
  20. Proftpd配置文件

热门文章

  1. 科普丨机器学习翻译和谷歌翻译算法
  2. html如何制作正方体手工图,怎么用卡纸做正方体(做长方体的步骤图纸)
  3. docker 使用 iso虚拟光驱一键式安装
  4. Google Chrome 75.0.3770.100 插件丰富且自带翻译的浏览器
  5. 发SCI,审稿人意见有一条是“English should be improved”,应该怎样回复?
  6. 终于可以不用radmin了
  7. 如何更改计算机上面的图表形式,excel表格数据转换图形-怎么把EXCEL中的一组数字转换成图表形式...
  8. 0pyqt获取textEdit控件的文本
  9. Linux 磁盘划分 LVM 逻辑分区管理步骤
  10. 基金的估值原来这么简单,一文看懂