设计模式 - 装饰者模式(Decorator)

      可能实战经验不多的原因,关于设计模式一直都是看完觉得很厉害,思想很巧妙,简直棒棒的。但是在项目或者程序中不知道如何使用。。。但是做一个总结记录总是没错的,多思考,多总结。   

------------------------------------------------------------------------------分割线  

      什么叫做装饰?从字面意思上来看的话,"装饰",就是将一个东西包装起来,拥有更多的特征,让这个物品变得更加好看。而装饰者设计模式就是将一个对象动态地去扩展其功能,让这个对象拥有更多的特性,让这个对象的功能变的更加多样,前提是在不改变原类文件的基础上扩展功能的。

例子:

      比如说在我们生活中使用的手机,现提供给了我们一款手机,具有基本的打电话,发短信,玩游戏的功能,代码示例如下:

手机类的功能接口:(定义了原始要被装饰的物品的功能)

package com.decorator;
/**
 * 手机类接口
 * @author lsy
 *
 */
public interface Cellphone {
	/**
	 * 打电话
	 */
	public void call();
	/**
	 * 发短信
	 */
	public void send();
	/**
	 * 玩游戏
	 */
	public void play();
}

实现接口的一类手机:(最原始的物品)

package com.decorator;
/**
 * 一个品牌的手机
 * @author lsy
 *
 */
public class HWPhone implements Cellphone{
	@Override
	public void call() {
		System.out.println("打一个电话");
	}
	@Override
	public void send() {
		System.out.println("发一条短信");
	}
	@Override
	public void play() {
		System.out.println("去玩游戏-俄罗斯方块");
	}
}

我们来测试最普通的手机:

    @Test
	public void test01(){
		Cellphone phone = new HWPhone();
		phone.call();
		phone.send();
		phone.play();
	}
------------------------------------
    打一个电话
    发一条短信
    去玩游戏-俄罗斯方块

      此时我们可以看到,手机有了,其功能有三个,打电话,发短信,玩游戏。

      那么现在我们要提新要求了,这个手机能不能打视频电话呀? 我想要打视频电话。问题来了,在不改变原手机类的前提下,我怎么让它变成一个有视频功能的手机?所以就想到了继承,我用一个VideoPhone类继承这个HWPhone ,然后重写call()方法,我不就可以让有打视频电话的功能了么?

      这个方法完全可以实现,但是问题在于,随着我们的手机功能越来越多,随着一个一个的功能添加,在手机本身结构不变的情况下,我们需要写很多很多的继承类,而且我的这些继承类之间并没有办法随意组合,而且在组合完毕后如果我要弃用一些功能,那将是一个庞大的工程。效果会是什么样子的,来看一张《Head first 设计模式》的图:

223147_OLzt_3347215.png

如果我们的项目变成了这个样子,不如删掉重写。

      那么我们就需要引入一个设计模式:装饰者模式。这个装饰者基类PhoneDecorator 一般是一个抽象类(也可能不是),它持有一个真实对象的引用,实现了和实体类HWPhone 同样的接口。

      为什么是是一个抽象类呢,在后面将会看到,后面每一个装饰器都继承了这个抽象类,每一个装饰器都是对单个功能进行了新的添加,而其他的功能没有明显的改变,我们使用这个抽象类将所有的方法抽象出来,每一个装饰器只需要重写控制增加单个功能的方法就可以了,其他未改变的方法调用父类(也就是这个抽象类)中的方法即可。

      抽象类实现Cellphone接口,是为了方法全部委托给该接口调用,目的是交给该接口的实现类即子类(每一个装饰器)进行调用。

装饰者抽象类型的基类

package com.decorator;
/**
 * 装饰者
 * @author lsy
 */
public abstract class PhoneDecorator implements Cellphone {
	
    //持有一个CellPhone接口的引用
	private Cellphone cp;
	
	public PhoneDecorator(Cellphone cp){
		this.cp = cp;
	}
		
	@Override
	public void call() {
		cp.call();
	}

	@Override
	public void send() {
		cp.send();
	}

	@Override
	public void play() {
		cp.play();
	}

}

下面就是装饰器了,这些一个一个的装饰器就对原本的手机进行了功能上的扩展:

扩展视频功能:

package com.decorator;
/**
 * 手机 的包装器1  (可视频的手机)
 * @author lsy
 *
 */
public class VideoPhone extends PhoneDecorator{
	
	public VideoPhone(Cellphone cp) {
		super(cp);
	}

	@Override
	public void call() {
		super.call();
		System.out.println("--加装了摄像头,再打一个视频电话....");
	}
}

扩展彩信功能:

package com.decorator;
/**
 * 手机 的包装器2  (可发送彩信的手机)
 * @author lsy
 *
 */
public class MMSPhone extends PhoneDecorator {

	public MMSPhone(Cellphone cp) {
		super(cp);
	}

	@Override
	public void send() {
		super.send();
		System.out.println("--加装了发彩信的功能,再发一条彩信....");
	}


}

扩展VR功能:

package com.decorator;
/**
 * 手机 的包装器3  (可玩VR游戏的手机)
 * @author lsy
 *
 */
public class GamePhone extends PhoneDecorator{

	public GamePhone(Cellphone cp) {
		super(cp);
	}

	@Override
	public void play() {
		super.play();
		System.out.println("--加装了手机VR设备,再玩一会儿VR游戏....");
	}

}

      可以看到,这三个装饰器都继承了装饰者基类PhoneDecorator ,所以它们也实现了CellPhone接口,装饰器本身也是一个CellPhone,所以装饰器本身也是可以被装饰的,这就可以在功能的随意组合上起到了关键作用。

我们现在来测试装饰过视频电话后的手机:

	@Test
	public void test02(){
		//增强版phone 视频电话的
		VideoPhone videoPhone = new VideoPhone(new HWPhone());
		videoPhone.call();
		videoPhone.send();
		videoPhone.play();
	}
------------------------
    打一个电话
    --加装了摄像头,再打一个视频电话....
    发一条短信
    去玩游戏-俄罗斯方块

      可以看到手机的功能经过VideoPhone类包装后,已经具有了视频电话的功能。

      那么现在我需要既能视频电话,又能彩信,又能玩VR游戏的手机:

      没问题!只要进行装饰器的层层包装就行了!

	@Test
	public void test04(){
		//增强版phone 既能视频又能彩信又能玩虚拟体感游戏
		GamePhone gamePhone = new GamePhone(new VideoPhone(new MMSPhone(new HWPhone())));
		gamePhone.call();
		gamePhone.send();
		gamePhone.play();
	}
--------------------
    打一个电话
    --加装了摄像头,再打一个视频电话....
    发一条短信
    --加装了发彩信的功能,再发一条彩信....
    去玩游戏-俄罗斯方块
    --加装了手机VR设备,再玩一会儿VR游戏....

      每一个装饰器都有一个构造方法调用super(cp),构造函数的参数是Cellphone接口,只要是该Cellphone的实现类都可以传递进去,可以进行层层包装,那么就会有了最后测试类中的的GamePhone gamePhone = new GamePhone(new VideoPhone(new MMSPhone(new HWPhone())));

      装饰器之间没有先后顺序,只有存在不存在这层装饰的概念。也就是说,如果不想要那个功能,把这层装饰去掉就好了,丝毫不影响其他的。

装饰者模式的应用:

     1  典型的:I/O流,很多类,超级多的类看着API看的头昏,I/O流就是可以通过层层地包装进行读取文件,像缓冲流就是将字符流进行了包装等等。

      2 想起来以前用GUI做的飞机大战,我方飞机可以有一个炮筒,两个炮筒,升级飞机什么的,这样就可以在飞机类不动的情况下使用装饰者模式进行装饰呀,而且可以层层包装,无限制地升级和降级。

小总结:

      1  、被装饰者是最原始的物品,装饰者模式就是用一个一个的装饰器将被装饰者一层一层地包装起来,然后就成了想要的样子。

      2、装饰者设计模式设计的原则是:对扩展开放、对修改关闭。这句话在体现在装饰者模式就是对于想要在基础功能上扩展的时候,只需要装饰器继承装饰者的抽象类,实现新的功能或者延展功能就可以对原物品(对象)进行包装扩展,对于修改原类的代码则是关闭的。

      3、装饰器本身是没有价值的,必须依赖物品(对象)本身。就像例子中的手机,没有了手机,你空有一个摄像头,怎么让别人视频,没有了手机,你空有一个手机VR控制,怎么让人家玩VR游戏?装饰器离不开最原本的物品本身。

      4、不只是一个装饰器整体包装了物品,而是各个装饰器的组合可以让物品变的更强大,不受任何限制,我们可以随意的组合,可以随意的拆分。

 

以上,对于装饰者模式的见解。

 

 

转载于:https://my.oschina.net/u/3347215/blog/902301

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值