前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(九十)建造者模式

Android开发笔记(九十)建造者模式

作者头像
aqi00
发布2019-01-18 14:05:54
6600
发布2019-01-18 14:05:54
举报
文章被收录于专栏:老欧说安卓老欧说安卓

基本概念

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

公开属性

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

代码语言:javascript
复制
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方法读取属性值。示例代码如下:

代码语言:javascript
复制
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类改造为建造者模式的代码例子:

代码语言:javascript
复制
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进行构建:

代码语言:javascript
复制
    public static Uri withAppendedPath(Uri baseUri, String pathSegment) {
        Builder builder = baseUri.buildUpon();
        builder = builder.appendEncodedPath(pathSegment);
        return builder.build();
    }

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

代码语言:javascript
复制
        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的用法代码例子:

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

Notification

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

代码语言:javascript
复制
		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的用法代码例子:

代码语言:javascript
复制
		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,最后执行anim4
		animSet.setDuration(5000);
		animSet.start();

图片缓存框架

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

代码语言:javascript
复制
	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开发笔记的完整目录

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年04月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本概念
    • 公开属性
      • 公开方法
        • 建造者模式
        • 初级用法
        • Android中的使用场合
          • AlertDialog
            • Notification
              • AnimatorSet
                • 图片缓存框架
                相关产品与服务
                验证码
                腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档