[关闭]
@xujun94 2016-08-05T08:29:52.000000Z 字数 8945 阅读 1616

建造者模式(Builder)及其应用


转载请注明原博客地址:

其实建造者模式在我们平时写项目的时候我们经常看到,大部分人没仔细观察或者不熟悉建造者模式,才忽略了它,在我们常用的ImageLoader和Rxjava里面其实都运用了建造者模式

本篇博客主要讲解一下几个问题
1. 什么是建造者模式
2. 建造者模式的应用场景,小Demo及优缺点分析
3. 建造者模式在Android源码中的体现
4. 建造者模式在常见的开源框架中ImageLoader和Rxjava的体现

1)什么是建造者模式

概念

简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。

类UML图

Builder:可以是一个接口或者一个抽象类,主要是统一标准,便于我们使用多态而已
ConcreteBuilder:Builder的实现类,主要负责组建Product。
导演类(Director):主要负责统一指挥构建组装Product,但是它是不知道Product里面具体是怎样做的。
Product:产品类,是我们需要建造的复杂对象,可以是抽象类也可以是具体类,视对象的复杂程度而定

2) 建造者模式的应用场景,小Demo及优缺点分析

假设我们现在是手机生产商,主要负责手机的组装,手机的品牌有挺多的,有索尼,三星,小米等品牌,同一个品牌的手机的分辨率,CPU,摄像头肯能一样也可能不一样,你会怎么做呢?

有人说用抽象工厂模式?
刚开始想这样也是挺好的?
对于不同的手机品牌,我们单独有一个工厂实现子类,接着再交给工厂子类自己去实现手机。

不过后面自己想了一下,工厂模式诞生的初衷只是负责生产对象,而不必要知道生产手机的细节,而题目中要求同一个品牌的手机的分辨率,CPU,摄像头,这不就要求我们需要生产手机的细节了吗?所以使用抽象工厂模式不是最好的选择

下面我们来看一下使用Builder模式设计的UML图

下面我们一起来看一下例子代码

抽象Product类

  1. //抽象Product类
  2. public abstract class Phone {
  3. protected String mCPU;
  4. protected String mCamera;
  5. protected String mScreen;
  6. protected String mSystem;
  7. public abstract void setSystem();
  8. public void setmCPU(String cpu){
  9. this.mCPU=cpu;
  10. }
  11. public void setmCamera(String camera) {
  12. this.mCamera = camera;
  13. }
  14. public void setmScreen(String screen) {
  15. this.mScreen = screen;
  16. }
  17. }

具体Product类

  1. /**
  2. * ContreteProduct类
  3. * @author xujun
  4. *
  5. */
  6. public class SonyPhone extends Phone {
  7. @Override
  8. public void setSystem() {
  9. mSystem="Android";
  10. }
  11. @Override
  12. public String toString() {
  13. return "SonyPhone [mCPU=" + mCPU + ", mCamera=" + mCamera
  14. + ", mScreen=" + mScreen + ", mSystem=" + mSystem + "]";
  15. }
  16. }

抽象Builder类

  1. /**
  2. * 抽象Builder类
  3. * @author xujun
  4. *
  5. */
  6. public abstract class Builder {
  7. public abstract void builderScreen(String screen);
  8. public abstract void buildCamera(String camrea);
  9. public abstract void buildCpu(String cpu);
  10. /**
  11. * 获得我们建造的Product对象
  12. * @return
  13. */
  14. public abstract Phone creat();
  15. public abstract void buildSystem();
  16. }

Builder类实现类

  1. public class SonyBuilder extends Builder {
  2. SonyPhone sonyPhone;
  3. public SonyBuilder() {
  4. sonyPhone=new SonyPhone();
  5. }
  6. @Override
  7. public void builderScreen(String screen) {
  8. sonyPhone.setmScreen(screen);
  9. }
  10. @Override
  11. public void buildCamera(String camrea) {
  12. sonyPhone.setmCamera(camrea);
  13. }
  14. @Override
  15. public void buildCpu(String cpu) {
  16. sonyPhone.setmCPU(cpu);
  17. }
  18. @Override
  19. public Phone creat() {
  20. return sonyPhone;
  21. }
  22. @Override
  23. public void buildSystem() {
  24. sonyPhone.setSystem();
  25. }
  26. }

Director类

  1. public class Director {
  2. Builder mBuilder;
  3. String mScreen="1920x720";
  4. String mCPU="双核";
  5. String mCamera="默认品牌摄像头";
  6. public Director(Builder builder){
  7. mBuilder=builder;
  8. }
  9. //在这个方法里面调用builder相应的方法
  10. public void construct(String camera,String screen,String cpu){
  11. mBuilder.buildCamera(camera);
  12. mBuilder.builderScreen(screen);
  13. mBuilder.buildCpu(cpu);
  14. mBuilder.buildSystem();
  15. }
  16. public void construct(String camera){
  17. this.construct(camera, mScreen, mCPU);
  18. }
  19. public void construct(){
  20. this.construct(mCamera,mScreen,mCPU);
  21. }
  22. }
  1. public class TestBuilder {
  2. public static void main(String[] str){
  3. Builder builder = new SonyBuilder();
  4. //导演持有builder的引用
  5. Director director = new Director(builder);
  6. director.construct("索尼摄像头", "1280x720像素", "4核因特尔CPU");
  7. Phone phone = builder.creat();
  8. System.out.println(phone.toString());
  9. director.construct("索尼摄像头");
  10. Phone phone2 = builder.creat();
  11. System.out.println(phone2.toString());
  12. director.construct();
  13. Phone phone3 = builder.creat();
  14. System.out.println(phone3.toString());
  15. }
  16. }

运行以上测试程序,将可以看到以下的输出结果

SonyPhone [mCPU=4核因特尔CPU, mCamera=索尼摄像头, mScreen=1280x720像素, mSystem=Android]
SonyPhone [mCPU=双核, mCamera=索尼摄像头, mScreen=1920x720, mSystem=Android]
SonyPhone [mCPU=双核, mCamera=默认品牌摄像头, mScreen=1920x720, mSystem=Android]

分析与讨论

  1. director.construct("索尼摄像头", "2560*1920", "16核因特尔CPU");

综上所述,建造者模式的 优点可概括如下

在实际中的引用

其实在实际项目开发中,我们往往用不到上面的标准的Builder模式,因为我们的对象往往没有那么复杂,假如我们现在手机生产商只复制生产索尼手机,那Builder设计的UML图可以简略如下,去除Director。改为下面的链式调用。

  1. SonyPhone phone=new SonyBuilder().buildCamera("索尼摄像机").buildCPU("8核").buildScreen("1280*720");

缺点

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

适用场景


3)Builder在Android中源码的体现。

还记得我们平时在使用Dialog的时候的这种用法吗?

  1. new AlertDialog.Builder(this).setCancelable(true)
  2. .setIcon(R.mipmap.app_logo).setMessage("温馨提醒").show();

哈哈,你是不是惊喜的发现这与我们上面简化的Builder模式几乎是一样的?没错,其实AlertDialog里面也使用了Builder模式

下面我们一起来看一下AlertDialog的源码

  1. // AlertDialog
  2. public class AlertDialog extends Dialog implements DialogInterface {
  3. // Controller, 接受Builder成员变量P中的各个参数
  4. private AlertController mAlert;
  5. // 构造函数
  6. protected AlertDialog(Context context, int theme) {
  7. this(context, theme, true);
  8. }
  9. // 4 : 构造AlertDialog
  10. AlertDialog(Context context, int theme, boolean createContextWrapper) {
  11. super(context, resolveDialogTheme(context, theme), createContextWrapper);
  12. mWindow.alwaysReadCloseOnTouchAttr();
  13. mAlert = new AlertController(getContext(), this, getWindow());
  14. }
  15. // 实际上调用的是mAlert的setTitle方法
  16. @Override
  17. public void setTitle(CharSequence title) {
  18. super.setTitle(title);
  19. mAlert.setTitle(title);
  20. }
  21. // 实际上调用的是mAlert的setCustomTitle方法
  22. public void setCustomTitle(View customTitleView) {
  23. mAlert.setCustomTitle(customTitleView);
  24. }
  25. public void setMessage(CharSequence message) {
  26. mAlert.setMessage(message);
  27. }
  28. // AlertDialog其他的代码省略
  29. // ************ Builder为AlertDialog的内部类 *******************
  30. public static class Builder {
  31. // 1 : 存储AlertDialog的各个参数, 例如title, message, icon等.
  32. private final AlertController.AlertParams P;
  33. // 属性省略
  34. public Builder(Context context) {
  35. this(context, resolveDialogTheme(context, 0));
  36. }
  37. public Builder(Context context, int theme) {
  38. P = new AlertController.AlertParams(new ContextThemeWrapper(
  39. context, resolveDialogTheme(context, theme)));
  40. mTheme = theme;
  41. }
  42. // Builder的其他代码省略 ......
  43. // 2 : 设置各种参数
  44. public Builder setTitle(CharSequence title) {
  45. P.mTitle = title;
  46. return this;
  47. }
  48. public Builder setMessage(CharSequence message) {
  49. P.mMessage = message;
  50. return this;
  51. }
  52. public Builder setIcon(int iconId) {
  53. P.mIconId = iconId;
  54. return this;
  55. }
  56. public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
  57. P.mPositiveButtonText = text;
  58. P.mPositiveButtonListener = listener;
  59. return this;
  60. }
  61. public Builder setView(View view) {
  62. P.mView = view;
  63. P.mViewSpacingSpecified = false;
  64. return this;
  65. }
  66. // 3 : 构建AlertDialog, 传递参数
  67. public AlertDialog create() {
  68. // 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog
  69. final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
  70. // 5 : 将P中的参数应用的dialog中的mAlert对象中
  71. P.apply(dialog.mAlert);
  72. dialog.setCancelable(P.mCancelable);
  73. if (P.mCancelable) {
  74. dialog.setCanceledOnTouchOutside(true);
  75. }
  76. dialog.setOnCancelListener(P.mOnCancelListener);
  77. if (P.mOnKeyListener != null) {
  78. dialog.setOnKeyListener(P.mOnKeyListener);
  79. }
  80. return dialog;
  81. }
  82. }
  83. }
  1. public AlertDialog show() {
  2. final AlertDialog dialog = create();
  3. dialog.show();
  4. return dialog;
  5. }

4)Builder模式在ImageLoader和Rxjava中的体现

在ImageLoader中的体现

  1. ImageLoaderConfiguration.Builder mBuilder = new ImageLoaderConfiguration
  2. .Builder(this)
  3. .threadPoolSize(3) //线程池内加载的数量
  4. .threadPriority(Thread.NORM_PRIORITY - 2) //设置当前线程的优先级
  5. .denyCacheImageMultipleSizesInMemory() //拒绝缓存同一图片的多个尺寸版本
  6. .tasksProcessingOrder(QueueProcessingType.FIFO) //队列的排队算法,默认FIFO。FIFO表示先进先出,LIFO表示后进先出
  7. .memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024)) //你可以通过自己的内存缓存实现
  8. .memoryCacheSize(2 * 1024 * 1024) //使用的内存大小
  9. .memoryCacheSizePercentage(13) //使用的内存百分比
  10. .memoryCacheExtraOptions(480, 800) //设置内存中图片的长宽
  11. .diskCache(new UnlimitedDiskCache(imageCacheDir)) //自定义磁盘的路径
  12. .diskCacheSize(50 * 1024 * 1024) //使用的磁盘大小
  13. .diskCacheFileCount(100) //磁盘的文件数量上限
  14. .imageDecoder(new BaseImageDecoder(false)) //对图片解码,如需缩放或旋转可在此处理
  15. .writeDebugLogs() //打印调试日志。上线时需要去掉该方法
  16. ;
  17. ImageLoader.getInstance().init(mBuilder.build());

在Rxjava中的体现

  1. Network.getInstance()
  2. .getApi()
  3. .getSMS(phoneNumber,smsType)
  4. .subscribeOn(Schedulers.io())
  5. .observeOn(AndroidSchedulers.mainThread())
  6. .subscribe(new Action1<SMSBean>() {
  7. @Override
  8. public void call(SMSBean securityBean) {
  9. LUtils.e("" + securityBean.toString());
  10. }
  11. }, new Action1<Throwable>() {
  12. @Override
  13. public void call(Throwable throwable) {
  14. EventBus.getDefault().post(err, GET_VERIFYCODE_ERR_SETPHONENUMBER);
  15. }
  16. });

到此今天博客的分析为止


题外话

以前为了应付面试,显得自己比较厉害,有时候一天下来生硬地记下了好几个设计模式,关于各种设计模式的应用场景和优缺点,那时候刚开始的一两天还记得,可过了几天,知识就还给了书本,基本都忘记了,更谈不上运用。记得有一次去面试,问了一个我以前一直认为很简单的单例模式,那时候叫我写出几种单例模式,我嗖的一声就写出类, 饿汉式,懒汉式(双重锁定,静态内部类),登记式。但当面试官问到为什么要使用Synchronized Class而不使用Synchronized this,我竟然搭不上,我知道this是代表当前实例的引用,class是锁住整个Class类,但就是区分不出来。后面回来复习了才知道。

从那以后,我开始静下心来,不再为了变得牛逼而去学习某样东西,这样目的性太强的话我们往往会带有一些浮躁,刚开始的一两个月可能你还能坚持下来了,可一旦你发现你学习了这几样东西,其实你还是跟大多数人一样,这时候的你可能就会浮躁,开始沉不住气了。继而自己开始懈怠,慢慢地你会发现你很慢就坚持静下心来学习了。

一句话,技术这种东西急不来,常常太用力的人跑不远,因为他们往往很难坚持下来。所以,从现在开始,静下心来,一步一个脚印,享受生活,享受编程给我们带来的乐趣。

卖一下广告,有兴趣了解设计模式的,了、可以阅读下面我的两篇博客

装饰者模式及其应用

观察者设计模式 Vs 事件委托(java)

转载请注明原博客地址:

例子源码下载地址:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注