源代码:https://gitee.com/AgentXiao/DecroatorPattern
要点:
1、装饰模式解决的问题
2、装饰模式的各个模块实现
3、装饰模式的优缺点
4、装饰模式和桥接模式的区别
一、场景引入
如上图所示,有一个狗(Dog)类,下面有三个子类,分别是会飞的狗(FlyDog)、会游泳的狗(SwimDog)、会跑的狗(RunDog)。假设此时三个子类都是单一功能的,也就说FlyDog只会飞,游泳和跑步都不会。
如果此时需要一个既会飞也会跑的狗,我们可以通过继承的方式实现,一次类推,可以得到:
这显然不是最好的方法!当功能非常多时,需要继承父类的子类数量将会大大增加!而装饰模式就是解决这个问题的一种设计模式。
二、装饰模式
装饰模式是一种用于代替继承的技术,无需通过继承增加子类就能够扩展对象的新功能。使用对象的组合关系(区分于“组合设计模式”)代替继承关系,避免类型体系的快速膨胀。
简单地说,装饰模式用于动态增加一个对象的新功能,或成为功能增强。
装饰模式实现细节:
(1)抽象构建角色Component。这是真实对象和装饰对象都需要实现的接口,便于客户端可以使用相同的方式交互装饰对象和真实对象,指上文的Dog。
(2)真实对象ConcreteComponent。具体需要装饰的对象,定义为MyDog。
(3)装饰对象Decorator。持有一个抽象构件的引用(核心)。装饰对象接受所有客户端的请求,并把这些请求通过引用转发给真实对象。这样,就能在真实对象调用前后增加新的功能,定义为SuperDog。
(4)具体装饰对象ConcreteDecorator:指上文的Fly、Swim、Run。
三、装饰模式的实现
1、抽象角色
/**
* @InterfaceName Dog
* @Description 抽象构建角色
* @Author xwd
* @Date 2018/10/25 9:52
*/
public interface Dog {
/**
* @MethodName showPower
* @Descrition 展示能力
* @Param []
* @return void
*/
void showPower();
}
2、具体角色
/**
* @ClassName MyDog
* @Description 具体对象
* @Author xwd
* @Date 2018/10/25 9:53
*/
public class MyDog implements Dog{
@Override
public void showPower() {
System.out.println("我还没有什么能力!");
}
}
3、装饰角色(核心)
/**
* @ClassName SuperDog
* @Description 装饰对象
* @Author xwd
* @Date 2018/10/25 9:54
*/
public class SuperDog implements Dog{
protected Dog dog;//持有抽象构建角色的引用
public SuperDog(Dog dog) {
this.dog = dog;
}
@Override
public void showPower() {
dog.showPower();
}
}
4、具体的装饰角色
/**
* @ClassName Fly
* @Description 飞行能力
* @Author xwd
* @Date 2018/10/25 9:57
*/
public class Fly extends SuperDog{
public Fly(Dog dog) {
super(dog);
}
@Override
public void showPower() {
super.showPower();
System.out.println("我具备飞行功能!");
}
}
/**
* @ClassName Run
* @Description 奔跑能力
* @Author xwd
* @Date 2018/10/25 9:57
*/
public class Run extends SuperDog{
public Run(Dog dog) {
super(dog);
}
@Override
public void showPower() {
super.showPower();
System.out.println("我具备奔跑功能!");
}
}
/**
* @ClassName Swim
* @Description 游泳能力
* @Author xwd
* @Date 2018/10/25 9:57
*/
public class Swim extends SuperDog{
public Swim(Dog dog) {
super(dog);
}
@Override
public void showPower() {
super.showPower();
System.out.println("我具备游泳功能!");
}
}
5、测试
/**
* @ClassName Client
* @Description 测试装饰模式
* @Author xwd
* @Date 2018/10/25 9:59
*/
public class Client {
public static void main(String[] args) {
//原生态的狗,还没有什么能力
Dog dog = new MyDog();
dog.showPower();
System.out.println("*********************");
//添加了装饰模式的狗,但是还没有添加具体功能
SuperDog superDog = new SuperDog(dog);
superDog.showPower();
System.out.println("*********************");
//添加了飞行能力的狗
SuperDog flyDog = new Fly(dog);
flyDog.showPower();
System.out.println("*********************");
//添加了游泳能力的狗
SuperDog swimDog = new Swim(dog);
swimDog.showPower();
System.out.println("*********************");
//既添加了飞行能力又添加了奔跑能力的狗
SuperDog runDog = new Run(dog);
SuperDog fsDog = new Fly(runDog);
fsDog.showPower();
System.out.println("*********************");
}
}
6、测试结果
由此可见,需要为一个对象添加新功能时,只需要建立这个功能,将具体对象传入。如果传入的对象是已经具备某些功能的,就相当远在那个基础上添加新的功能。
四、开发中使用的场景
- IO中输入流和输出流的设计。
- Swing包中图形界面构件功能。
- Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
- Struts2中,request,response,session对象的处理。
在这里只解释其中一种:
在IO流中,抽象构建对象是InputStream、OutputStream、Reader、Writer。
真实对象是FileInputStream、FileOutputStream。
装饰对象是FilterInputStream、FilterOutputStream。
具体的装饰对象:BufferedOutputStream、BufferedInputStream等。
五、总结
1、功能
装饰模式(Decorator)也叫包装器模式(Wrapper)。
装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类。
2、优点
- 扩展对象功能,比继承灵活,不会导致类个数急剧增加。
- 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。
- 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。
3、缺点
- 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
- 装饰模式易于出错,调试排查比较麻烦。
4、装饰模式和桥接模式的区别
两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。
- 桥接模式是对象自身现有机制沿着多个维度变化。
- 装饰模式是为了增加新的功能。