装饰模式(Decorator)简介

装饰模式是第三个介绍的模式了.

这个模式没有前面两个那么好理解.,


一, 装饰模式(decorator)的定义.

教材里是这样写的:

动态第给1个对象添加1写额外的职责(功能), 就增加的功能来讲, 装饰模式比生成子类更加灵活.


就咁睇的确很难明白. 但我们起码可以知道一下两点:

1. 装饰模式的作用是增强1个对象(类) 的功能.

2. 它比生成子类更加灵活.


本文着重讲下第2点, 包括什么是生成子类,  为什么装饰模式比它更加灵活.



二, 1个Hunter1(猎人)类的例子.

举1个很简单的例子. 1个猎人类实现1个打猎的接口.


Huntable 接口:

public interface Huntable {
	public void hunt();
}

猎人类Hunter1:

public class Hunter1 implements Huntable{
	public void eat(){
		System.out.println("eating!");
	}
	
	public void hunt(){
		System.out.println("Hunter is Hunting!");
	}
}


可见Hunter1这个类实现了Huntable借口. 

hunt()方法里只简单重写了hunt()方法.


客户端代码:

Hunter1 h = new Hunter1();
h.hunt();

输出:

Hunter is Hunting!



1个增强hunt()方法的需求:

好了, 现在有1个猎人, 他出去不只是打猎,  他打猎的同时还同时训练它的猎狗, 最后还会给战利品拍一张照片.

也就是需要增强hunt()方法功能.


加上

trainDog() 和 takePic()这个两个方法.


怎么实现呢?


三, 通过添加子类Hunter2方法来增强Hunter1

open-closed principle:

可能有人会觉得. 直接在Hunter1里加上两个方法trainDog () 和  takePic(),  在hunt()里调用这个两个新方法不就完事了吗.


的确, 但是这种做法修改了原有的类,  违反了"封闭-开放原则". 

所谓"封闭-开发原则" 就是对修改封闭, 对扩展开放.


在项目中, 很多旧的业务类根本不会让你修改(jar包发布或者你没有权限)


所以我们应该把这个新需求当做1个扩展来处理, 而不是修改旧代码.


其中1个方法就是增加子类.



新增1个Hunter2 类:

public class Hunter2 extends Hunter1{
	@Override
	public void hunt(){
		super.hunt();
		this.trainDog();
		this.takePic();
	}
	
	//new function()
	public void trainDog(){
		System.out.println("Train the Dog!");
	}
	
	public void takePic(){
		System.out.println("Take a picture!");
	}
}



客户端代码:

Hunter2 h2 = new Hunter2();
h2.hunt();


UML:



可见, 这种方法, 只是新增了1个子类, 以及修改客户端代码, 对原来的类代码没有任何修改, 是符合"封闭-修改 原则"的.



这种修改, 增强了原来的类Hunter1 的hunt()功能, 可以说是"装饰"了原来的hunt()方法.

但是这种修改不是很灵活.


比如:   这种猎人是先打猎,再训狗,再拍照的.

            如果有一种猎人喜欢先拍照再训狗了,  我们就又要增加1个Hunter1的子类了....



而装饰模式能解决问题.


四, 通过装饰模式来增强Hunter类.

Huntable 接口 和 Hunter类:

首先构造两个相同的接口和类:

public interface Huntable {
	public void hunt();
}

public class Hunter implements Huntable {
	public void eat(){
		System.out.println("eating!");
	}

	@Override
	public void hunt(){
		System.out.println("Hunter is Hunting!");
	}
}

Hunter类跟上面的Hunter1类是完全一样的.

下面开始构造新的类来强化hunt()方法.


抽象装饰角色类   HuntOperation:

无论, 是训狗trainDog() 还是拍照takePic() 都是对hunt()的增强.

我们新增1个HuntOperation类,  让它继承Hunter类,  让它能重写hunt()方法.

public abstract class HuntOperation extends Hunter{
	
	private Hunter hunter = null;
	
	//setting component
	public HuntOperation(Hunter hunter){
		this.hunter = hunter;
	}
	
	//actually execute component's hunt() method
	@Override
	public void hunt(){
		if (null != hunter){
			hunter.hunt();
		}
	}
}

注意, 这个类有1个Hunter对象成员.


在hunt()方法里, 执行的实际上是成员Hunter对象的hunt()方法.


具体的装饰行为类 HuntTakePic():

照相类, 就让它继承抽象行为类 HuntOperation类.

public class HuntTakePic extends HuntOperation{
	public HuntTakePic(Hunter hunter) {
		super(hunter);
	}

	@Override
	public void hunt(){
		super.hunt();
		this.takePic();
	}
	
	public void takePic(){
		System.out.println("Taking a Picture!");
	}
}

可见, 它先执行性成员Hunter对象的hunt()方法, 在执行自己的照相方法.


具体的装饰行为类 HuntTrainDog():

同样, 这个类也继承HuntOperation.

先执行Hunter对象的hunt()方法, 再执行自己的TrainDog方法.

public class HuntTrainDog extends HuntOperation{

	public HuntTrainDog(Hunter hunter) {
		super(hunter);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	public void hunt(){
		super.hunt();
		this.trainDog();
	}
	
	public void trainDog(){
		System.out.println("Training the dog!");
	}

}

客户端代码:

客户端代码如下:

先构建1个Hunter对象h, 然后再基于h构建HuntTakePic htp,  然后在基于htp构建HuntTrainDog 对象htd.

最后执行htd的hunt()方法.



	        Hunter h = new Hunter();
		HuntTakePic htp = new HuntTakePic(h);
		HuntTrainDog htd = new HuntTrainDog(htp);
		
		htd.hunt();


实际上, 就先执行Hunter对象的hunt(), 再执行HuntTakePic对象的hunt(), 最后才执行HuntTrainDog对象的hunt()

输出:

Hunter is Hunting!
Taking a Picture!
Training the dog!



UML:




装饰模式的灵活性:

好了, 这种模式一样能增强Hunter类的hunt()

那么它到底比第一种方法灵活在哪里呢.


很简单, 假如另1类猎人想调换训狗, 和照相的顺序, 只需修改客户端的构建循序, 并需要再新增1个子类.

这就是装饰模式的灵活性.


有人会觉得, 装饰模式同样需要增加子类啊,  实际上, 假如我们为Hunter的hunt()再增加1个子行为, 例如给猎物剥皮.

那么只需要增加1个抽象行为HuntOperation的子类, 而不是Hunter的子类..





五, 小结

其实想深一层, 装饰模式之所以叫装饰模式.

是因为装饰不能改变原有的功能的行为.


就如上面的例子,  Hunter里原来的hunt()方法没有改变,  只是增加了一些额外的功能.


所以如果需要从根本上改变Hunter里hunt()的代码, 装饰模式就不适用了,  还是建议用增加Hunter子类的方法.





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值