首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(九十三)装饰模式

Android开发笔记(九十三)装饰模式

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

基本概念

装饰模式是扩展功能用的一种设计模式, 一般要扩展功能,我们都会想到继承,可是继承只能继承一个基类,如果有多个条件需要分别进行扩展,那得写好几个派生类,条件越多派生类的数量也越多。 上面描述比较抽象,还是举个例子来说明。比如人分男人和女人,先建个Human基类,再建Man和Woman两个派生类。同时人又有不同国籍,比如说中国男人、日本女人等等,此时再创建ChinaMan、ChinaWoman、JapanMan、JapanWoman四个派生类,其中ChinaMan和JapanMan继承自Man类,ChinaWoman和JapanWoman继承自Woman类。同时,同一国籍的人又有相同的行为动作,比如说中国人写中文,日本人写日文,所以ChinaMan和ChinaWoman理应继承自一个名为中国人的类,JapanMan和JapanWoman理应继承自一个名为日本人的类;但现实情况是,ChinaMan继承自Man类,ChinaWoman继承自Woman类,已经无法再继承其他类了,因此只能在这两个类中各自实现中国人的动作,当然实现一个中国人的接口也是办法。 为解决上面这个窘境,我们可以引入装饰模式加以优化。装饰模式把成员分为四个角色: 1、抽象基类:定义该集合将要使用的基本属性和方法。 2、初步实现的派生类:由抽象基类简单派生而来,并实现普通的构造函数。 3、待装饰的基类:定义抽象基类的一个实例,并实现一个基于对象的构造函数。 4、装饰好的派生类:由待装饰的基类派生出来,可进行定制化处理。

简单用法

讲概念很容易把人搞得一头雾水,博主也是一样,接下来还是直接上个代码例子好了,通过装饰模式实现男人、女人、中国人、美国人的交叉继承。 磨刀不误砍柴工,我们规划一下四个成员角色的类名分布,如下所示: 1、抽象基类:Human 2、初步实现的派生类:Person 3、待装饰的基类:People 4、装饰好的派生类:Man/Woman/Chinese/American 首先创建一个抽象基类Human,代码示例如下:

public abstract class Human {
	private String name;
	private String nation;
	private int sex;
	
	protected void setName(String name) {
		this.name = name;
	}

	protected String getName() {
		return this.name;
	}

	protected void setNation(String nation) {
		this.nation = nation;
	}

	protected String getNation() {
		return this.nation;
	}

	protected void setSex(int sex) {
		this.sex = sex;
	}

	protected int getSex() {
		return this.sex;
	}

	protected String showInfo() {
		String desc = String.format("%s是个%s人,%s是%s人", 
				getName(), (getSex()==0)?"男":"女", (getSex()==0)?"他":"她", getNation());
		return desc;
	}
	
}

然后创建初步实现的派生类Person,代码示例如下:

public class Person extends Human {
	public Person(String name) {
		setName(name);
	}
}

接着创建待装饰的基类People,代码示例如下:

public abstract class People extends Human {
	private Human mBase;

	public People(Human base) {
		mBase = base;
	}

	protected void setName(String name) {
		mBase.setName(name);
	}

	protected String getName() {
		return mBase.getName();
	}

	protected void setNation(String nation) {
		mBase.setNation(nation);
	}

	protected String getNation() {
		return mBase.getNation();
	}

	protected void setSex(int sex) {
		mBase.setSex(sex);
	}

	protected int getSex() {
		return mBase.getSex();
	}
	
}

再来创建各个装饰好的派生类,如男人的类Man、女人的类Woman、中国人的类Chinese、美国人的类American,代码示例如下: Man.java

public class Man extends People {

	public Man(Human base) {
		super(base);
		setSex(0);
	}

}

Woman.java

public class Woman extends People {

	public Woman(Human base) {
		super(base);
		setSex(1);
	}

}

Chinese.java

public class Chinese extends People {

	public Chinese(Human base) {
		super(base);
		setNation("中国");
	}

}

American.java

public class American extends People {

	public American(Human base) {
		super(base);
		setNation("美国");
	}

}

最后是装饰类的调用代码例子:

public class Wrapper {
	public static void main(String[] arg) {
		Human one = new Chinese(new Man(new Person("张三")));
		Human two = new Chinese(new Woman(new Person("邓丽君")));
		Human three = new American(new Man(new Person("乔布斯")));
		Human four = new American(new Woman(new Person("露茜")));
		System.out.println(one.showInfo());
		System.out.println(two.showInfo());
		System.out.println(three.showInfo());
		System.out.println(four.showInfo());
	}
}

下面是程序运行结果:

张三是个男人,他是中国人
邓丽君是个女人,她是中国人
乔布斯是个男人,他是美国人
露茜是个女人,她是美国人

Android中的使用场合

Android开发中不用装饰模式则已,一用装饰模式都是用在复杂之处,具体的说,主要有输入输出流、ContextWrapper两类例子。

输入输出流

java.io包下的InputStream和OutputStream类及其派生类,构成了装饰模式的一大家族,比如下面这行代码:

		BufferedReader input = new BufferedReader(new InputStreamReader(System.in));

分析可知,这行代码用到的类与装饰模式角色的对应关系如下: Reader类对应抽象基类,InputStreamReader类对应初步实现的派生类,BufferedReader类同时对应待装饰的基类与装饰好的派生类,因为这里业务不复杂,所以BufferedReader类同时身兼两职务。 另外一个io流的装饰模式角色如下: 1、抽象基类:InputStream 2、初步实现的派生类:FileInputStream 3、待装饰的基类:FilterInputStream 4、装饰好的派生类:BufferedInputStream/DataInputStream/LineNumberInputStream 下面是文件io流中运用装饰模式的代码例子,有关文件读写的介绍参见《Android开发笔记(三十三)文本文件和图片文件的读写》。

	private Bitmap openBitmap(String path) {
		Bitmap bitmap = null;
		try {
			BufferedInputStream bis = new BufferedInputStream(
					new FileInputStream(path));
			bitmap = BitmapFactory.decodeStream(bis);
			bis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bitmap;
	}

ContextWrapper

我们熟悉的Activity和Service就使用了装饰者模式,博主这种小白刚发现时连嘴巴都惊讶的合不拢了。我们先列出装饰模式四种角色与相关类的对应关系: 1、抽象基类:Context。Context是一个抽象类,里面包含了各种方法的声明,如setTheme、getPackageName、getSharedPreferences、getSystemService、startActivity、sendBroadcast、registerReceiver、unregisterReceiver、startService、stopService等等。 2、初步实现的派生类:ContextImpl。ContextImpl是真正实现Context的类,该类在android.jar里面找不到,要查看sdk的源码才能找到。 3、待装饰的基类:ContextWrapper。ContextWrapper是Context的包装器,里面定义了Context的一个实例mBase,以及基于Context对象的构造函数,内部所有的操作都是调用mBase的方法。 4、装饰好的派生类:Activity/Service/Application。这几个类都是从ContextWrapper派生而来。 Context使用装饰模式的好处,除了在Activity/Service/Application上体现外,还体现在如下两个方面: 1、BroadcastReciver。虽然它本身不是继承自Context,内部也没有Context实例,但是每当一个新广播到达时,框架都传递一个Context对象给onReceive()。这个Context对象是一个ReceiverRestrictedContext实例,它禁用了两个函数:registerReceiver()和bindService(),即在接收广播时不允许注册新广播,也不允许绑定服务。 2、ContentProvider。它本身也不是继承自Context,但是它可以通过getContext()函数返回一个Context对象。如果ContentProvider是在同一个进程中调用,getContext()将返回当前Application的实例。但是,如果ContentProvider在其它进程中调用,getContext()将返回一个代表运行包的新实例。 以上提到与Context相关的Android四大组件的介绍参见: 《Android开发笔记(三十九)Activity的生命周期》 《Android开发笔记(四十一)Service的生命周期》 《Android开发笔记(四十二)Broadcast的生命周期》 《Android开发笔记(五十四)数据共享接口ContentProvider点此查看Android开发笔记的完整目录

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本概念
  • 简单用法
  • Android中的使用场合
    • 输入输出流
      • ContextWrapper
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档