基本概念

建造者模式是一种常用的设计模式,它用于把类的表现和构建分离开来。引入建造者模式的缘由,且看博主下面细细道来。

公开属性

一般我们定义一个类的属性,如果属性是公开的,那可以直接对该类的属性赋值和取值。示例类的代码如下:

public class Person {public String name;public String password;public String birthday;public int age;
}

上面的Person类,属性都是公开的,这就带来几个问题:
1、属性名称被暴露了,如果现在要变更属性名称(比如说把name改名为username),那得把所有调用处的属性名都改过来;
2、有时根据业务需求得改变属性的赋值方式(比如说对password进行加密),那也得把所有调用处的属性赋值方式都改过来;
3、有些属性之间存在关联关系,改了一个属性之后,另一个属性值也要跟着变(比如修改了birthday字段,可能age字段也要跟着变);

公开方法

基于以上几个问题,我们在定义类时,一般都是通过set方法对属性赋值,通过get方法读取属性值。示例代码如下:

public class Person {private String name;private String password;private String birthday;private int age;public void setName(String name) {this.name = name.toUpperCase();  //用户名不区分大小写,统一转为大写}public String getName() {return this.name;}public void setPassword(String password) {this.password = password; //这里可补充加密操作}public String getPassword() {return this.password; //这里可补充解密操作}public void setBirthday(String birthday) {this.birthday = birthday; //这里可根据生日计算年龄,即自动对age字段赋值}public String getBirthday() {return this.birthday;}public void setAge(int age) {this.age = age;}public int getAge() {return this.age;}}

多数情况下,set方法与get方法联合使用,这就足够了。可是有时候,这个类还有其他的动作,比如说Person类还定义了登录动作,登录动作要对该用户的用户名和密码进行校验,如果用户名和密码输入正确,就返回登录成功。但实际业务往往不是这么简单,比如说用户登录期间可能还要输入短信验证码,然后在等待验证码短信时,用户密码因为某种原因发生变化(比如其他地方调用了setPassword方法),造成接收验证码之前密码校验通过,输入验证码之后密码校验反而失败。

建造者模式

为此,建造者模式应运而生,它把对象的表现与构建分离开来,即把对象的使用分为两个步骤:第一步输入各种参数进行构建,此时只能设置属性不能操作业务动作;第二步根据构建好的对象开展业务动作,此时不能修改属性设置。就像建筑公司修桥造路,根据设计方案确定了水泥、砂石与钢筋的用量,然后按部就班加工建筑材料进行施工。如果施工过程中,有人私自减少水泥用量,或者把钢筋换成水泥,这个工程多半要成为豆腐渣工程了。

建造者模式具体到代码实现上,是采用内部类的形式把构建部分分离出来,内部类的说明参见《 Android开发笔记(八十六)几个特殊的类》。即在Person类中再定义一个内部类Builder,由Builder类完成参数设置等构建操作。另外,为了确保对象的每个属性值只被赋值依次,可给各属性加上final修饰符,final的介绍参见《 Android开发笔记(八十七)几个修饰关键字》。下面是把Person类改造为建造者模式的代码例子:

public class Person {private final String name;private final String password;private final String birthday;private final int age;public String getName() {return this.name;}public String getPassword() {return this.password; //这里可补充解密操作}public String getBirthday() {return this.birthday;}public int getAge() {return this.age;}public void login() {//这里补充登录操作的代码}private Person(Builder builder) {this.name = builder.name;this.password = builder.password;this.birthday = builder.birthday;this.age = builder.age;}public static class Builder {private String name;private String password;private String birthday;private int age;public Builder setName(String name) {this.name = name.toUpperCase();  //用户名不区分大小写,统一转为大写return this;}public Builder setPassword(String password) {this.password = password; //这里可补充加密操作return this;}public Builder setBirthday(String birthday) {this.birthday = birthday; //这里可根据生日计算年龄,即自动对age字段赋值return this;}public Builder setAge(int age) {this.age = age;return this;}public Person build() {return new Person(this);}}}

初级用法

我们知道,java中构建字符串主要有下列几种方式:
1、几个字符串使用“+”连接;
2、调用String.format方法进行字符串格式化;
3、使用StringBuilder类构造字符串;
其中第三种方式便是建造者模式的初级模型。通过调用StringBuilder类的append、insert、delete等方法修改内容,最后调用toString方法输出构造完的字符串。

当然StringBuilder是单独的类,并非String类的内部类,而且也不是操作具体的属性,所以StringBuilder不是真正意义上的建造者模式。与StringBuilder比较类似,同时又采用建造者模式的,这个例子是Uri.Builder。不过Uri内部已经封装好了建造过程,没有向外开放Builder的使用,通常我们调用Uri.parse或者Uri.withAppendedPath方法即可获得Uri实例。

查看withAppendedPath方法的源码,可看到其内部采用了Builder进行构建:

    public static Uri withAppendedPath(Uri baseUri, String pathSegment) {Builder builder = baseUri.buildUpon();builder = builder.appendEncodedPath(pathSegment);return builder.build();}

其中buildUpon是个抽象方法,在具体类中需要重写。下面是StringUri类重写后的buildUpon方法,即可看到详细参数的建造过程:

        public Builder buildUpon() {if (isHierarchical()) {return new Builder().scheme(getScheme()).authority(getAuthorityPart()).path(getPathPart()).query(getQueryPart()).fragment(getFragmentPart());} else {return new Builder().scheme(getScheme()).opaquePart(getSsp()).fragment(getFragmentPart());}}

Android中的使用场合

Android中会用到建造者模式的场合,一般是与异步操作有关的。因为异步操作的等待时间较长,极有可能在等待过程中发生属性值变更的情况,所以为了避免属性变化导致处理异常,就要引入建造者模式。常见的建造者模式应用场景包括:对话框AlertDialog、通知推送Notification、集合动画AnimatorSet,以及图片缓存框架等等。

AlertDialog

AlertDialog的详细介绍参见《 Android开发笔记(六十六)自定义对话框》。下面是AlertDialog.Builder的用法代码例子:

     AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("今天天气真好啊");builder.setMessage("我们去哪里玩玩吧");builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {showToast("是呀,我们去吃顿大餐吧");}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {showToast("真不巧,我已经约了别人啦");}});builder.setNeutralButton("中立", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {showToast("嗯,今天我有事,明天可以吗");}});AlertDialog alert = builder.create();alert.show();

Notification

Notification的详细介绍参见《 Android开发笔记(五十二)通知推送Notification》。下面是Notification.Builder的用法代码例子:

     Notification.Builder builder = new Notification.Builder(this);builder.setContentIntent(contentIntent).setDeleteIntent(deleteIntent).setUsesChronometer(true).setProgress(100, 60, false).setSubText("这里是副本").setNumber(99).setAutoCancel(false).setSmallIcon(R.drawable.tt_s).setTicker("提示文本").setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.tt_s)).setContentTitle("标题文本").setContentText("内容文本");Notification notify = builder.build();NotificationManager notifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);notifyMgr.notify(R.string.app_name, notify);

AnimatorSet

AnimatorSet的详细介绍参见《 Android开发笔记(九十六)集合动画与属性动画》。下面是AnimatorSet.Builder的用法代码例子:

     ObjectAnimator anim1 = ObjectAnimator.ofFloat(tv_text, "alpha", 1f, 0.1f, 1f, 0.5f, 1f);ObjectAnimator anim2 = ObjectAnimator.ofFloat(tv_text, "rotation", 0f, 360f);ObjectAnimator anim3 = ObjectAnimator.ofFloat(tv_text, "scaleY", 1f, 3f, 1f);ObjectAnimator anim4 = ObjectAnimator.ofFloat(tv_text, "translationY", 0f, 300f);AnimatorSet animSet = new AnimatorSet();//AnimatorSet.Builder不提供create或build方法AnimatorSet.Builder builder = animSet.play(anim1);builder.with(anim2).after(anim3).before(anim4);// anim3先执行,然后再同步执行anim1、anim2,最后执行anim4animSet.setDuration(5000);animSet.start();

图片缓存框架

图片缓存框架的详细介绍参见《 Android开发笔记(七十七)图片缓存算法》。两个常见的图片缓存框架Picasso和Universal-Image-Loader都实现了建造者模式的内部类Builder,下面是ImageLoaderConfiguration.Builder的用法代码例子:

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

点此查看Android开发笔记的完整目录

Android开发笔记(九十)建造者模式相关推荐

  1. Android开发笔记(九十一)工厂模式

    基本概念 工厂模式是一种常用的实例化对象设计模式. 程序开发很多时候都在不停地敲if.else,因为业务需求总在发展变化,今天客户要求生产A产品,明天客户要求把A产品稍微改改变成B产品,当然A产品与B ...

  2. Android初级开发笔记-- activity启动模式的学习(1)

    第一次学习Android中一个很重要的概念,启动模式.文章记录的也只是一些入门知识,随着学习的深入还会有activity启动模式的学习(2)和(3). 下面分三个小点说一下对启动模式的理解区别以及如何 ...

  3. Android开发笔记(一百六十七)Android8.0的画中画模式

    前面的博文< Android开发笔记(一百五十九)Android7.0的分屏模式>介绍了Android7.0的多窗口特性,但是这个分屏的区域是固定的,要么在屏幕的上半部分,要么在屏幕的下半 ...

  4. Android开发笔记(一百六十)休眠模式下的定时器控制

    定时器AlarmManager常常用于需要周期性处理的场合,比如闹钟提醒.任务轮询等等.并且定时器来源于系统服务,即使App已经不在运行了,也能收到定时器发出的广播而被唤醒.似此回光返照的神技,便遭到 ...

  5. Android开发笔记(一百五十九)Android7.0的分屏模式

    现在的手机屏幕越来越大,使得在屏幕上同时开多个窗口不再奢侈,因此Android从7.0开始顺势推出了分屏功能,也被称作多窗口模式.比如把竖长的手机屏幕分成上下两个窗口,一边在上面的窗口中观看电影,一边 ...

  6. Android开发笔记(九十八)往图片添加部件

    添加圆角 添加圆角的功能,要用到Canvas类的drawRoundRect方法,即把画布裁剪成指定的圆角矩形. 下面是给图片添加圆角的效果截图: 下面是给图片添加圆角的代码片段: public sta ...

  7. Android开发笔记(九十六)集合动画与属性动画

    集合动画AnimationSet 补间动画有四大类:透明度动画AlphaAnimation.旋转动画RotateAnimation.缩放动画ScaleAnimation.平移动画TranslateAn ...

  8. Android开发笔记(九十五)自定义Drawable

    Drawable Bitmap是Android对图像的定义描述,而Drawable则是对图像的展现描述,在View视图中显示图像都是通过Drawable来实现的.其中有关Bitmap的介绍参见< ...

  9. Android开发笔记(九十四)图片的基本加工

    位图管理Bitmap Android上的图形使用Drawable类,而位图管理则使用Bitmap类,java上与之对应的是awt包中的BufferedImage.Android开发中有需要对jpg.p ...

最新文章

  1. 拜托,面试别再问我JVM了!!!
  2. 深入redis内部---网络编程
  3. 如何摆脱JavaFX中的重点突出显示
  4. java criteria and_criteria用法
  5. 修复Mysql主从不同步shell
  6. 2010年3月份第二周51aspx发布源码
  7. 【转】如何在windows平台开发OpenGL程序使用OpenGL1.2或更高版本
  8. 传输请求时报信息对象0REQ_CDATE不存在
  9. 项目开发计划——机房收费系统
  10. Azido-TAT,大环化合物,双功能螯合剂的性质
  11. 【毕设教程】python区块链实现 - proof of work工作量证明共识算法
  12. 英语字母演变——wsdchong
  13. 网络安全宣传月安全团队需要知道的关于PKI的九件事
  14. 在移动硬盘安装操作系统
  15. IDEA查看maven的依赖树
  16. 运用 Elasticsearch 8.1.x 实现智能问答系统
  17. ctfshow-命令执行-web38
  18. 游戏感虚拟感觉的游戏设计师_从零到游戏设计师:即使您没有任何经验,如何开始制作视频游戏...
  19. 兰州大学计算机学院研究生院导师,兰州大学研究生导师介绍:赵志光
  20. pypy的安装与使用

热门文章

  1. IntelliJ IDEA 创建Java Web项目
  2. 了解一下Bootstrap
  3. java 关闭阻塞线程池_如果优雅地关闭ExecutorService提供的java线程池
  4. 若依前端如何生成序号?
  5. JS如何关闭当前浏览器窗口?
  6. 学院教务管理系统oracle设计,浙工院教学教务管理信息系统的设计与实现
  7. [Ext JS ][12.13] FieldSet 与 Grid结合 ,实现FieldSet 显示Gird中Store 的数量
  8. Extjs 代码拾穗
  9. IE、FireFox、Opera三种浏览器Document对象的方法对比
  10. linux查看消息队列的状态,linux – 如何知道某个时间点在消息队列中收到的消息数...