Java设计模式——03装饰者模式
一、装饰者模式基本介绍
1、什么是装饰者模式
装饰模式(Decorator)指的是在不改变原类文件和不使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
2、装饰者模式的优缺点
优点:
- 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
- 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
- 装饰者模式有很好地可扩展性
缺点:
- 这种比继承更加灵活机动的特性,也同时意味着更多的复杂性。
- 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
- 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
3、使用场景
以下情况使用Decorator模式:
1. 需要扩展一个类的功能,或给一个类添加附加职责。
2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
4、装饰者模式的类图
在装饰模式中的各个角色有:
(1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
(2)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
(3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
(4)具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
二、装饰者模式的实现
1、需求描述
沙发可以坐、可以躺,我们还可以为它添加上按摩功能使它成为保健沙发,我们还可以为它添加上可以展开供人睡觉的功能让它变成沙发床。(关于直接使用继承方法,导致“类爆炸”问题,可以参考https://www.cnblogs.com/rookieFly-tdiitd/p/4914593.html)
2、具体实现
类图
(1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
package 装饰者模式;
//抽象构件(Component)角色
public interface Component_Sofa {
//测试方法,用以展示沙发所具有的功能
void show();
//坐
void sit();
//躺
void lie();
}
(2)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
package 装饰者模式;
/**
* 再定义一个Sofa接口的实现类,扮演装饰模式中的
* 具体构件(Concrete Component)角色
*/
public class ConcreteComponent_Sofa implements Component_Sofa {
@Override
public void show() {
this.sit();
this.lie();
}
@Override
public void sit() {
System.out.println("可以坐");
}
@Override
public void lie() {
System.out.println("可以躺");
}
}
(3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
package 装饰者模式;
/**
* 再定义一个装饰(Decorator)角色
* 持有一个构件(Component)对象的实例,
* 并实现一个与抽象构件接口一致的接口
*/
public class Decorator_Sofa implements Component_Sofa {
//私有的构件对象
private Component_Sofa sofa;
public Decorator_Sofa(Component_Sofa sofa) {
this.sofa = sofa;
}
@Override
public void show() {
// 委派给具体构件
sofa.show();
}
@Override
public void sit() {
// 委派给具体构件
sofa.sit();
}
@Override
public void lie() {
// 委派给具体构件
sofa.lie();
}
}
(4)具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
角色1:
package 装饰者模式;
/**
* 具体装饰(Concrete Decorator)角色:负责给构件对象添加上新功能。
*/
public class ConcreteDecorator_Bedsofa extends Decorator_Sofa {
public ConcreteDecorator_Bedsofa(Component_Sofa componentSofa) {
super(componentSofa);
}
@Override
public void show(){
super.show();
this.add();
}
void add(){
System.out.println("可以睡觉");
}
}
角色2:
package 装饰者模式;
public class ConcreteDecorator_Healthsofa extends Decorator_Sofa {
public ConcreteDecorator_Healthsofa(Component_Sofa sofa) {
super(sofa);
}
@Override
public void show() {
super.show();
this.add();
}
void add(){
System.out.println("可以按摩");
}
}
功能测试:
package 装饰者模式;
public class Sofa_Test {
public static void main(String[] args) {
//1、普通沙发
ConcreteComponent_Sofa sofa1 = new ConcreteComponent_Sofa();
System.out.println("普通沙发功能:");
sofa1.show();
System.out.println();
//2、BedSofa
ConcreteDecorator_Bedsofa bedsofa = new ConcreteDecorator_Bedsofa(sofa1);
System.out.println("Bed沙发功能:");
bedsofa.show();
System.out.println();
//3、HealthSofa
ConcreteDecorator_Healthsofa healthsofa = new ConcreteDecorator_Healthsofa(sofa1);
System.out.println("Health沙发的功能");
healthsofa.show();
System.out.println();
}
}
运行结果:
普通沙发功能:
可以坐
可以躺Bed沙发功能:
可以坐
可以躺
可以睡觉Health沙发的功能
可以坐
可以躺
可以按摩
Process finished with exit code 0
3、总结
装饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,装饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。
意图:动态地给一个对象添加一些额外的职责和增加功能。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
参考文档1:https://baike.baidu.com/item/%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F/10158540?fr=aladdin
参考文档2:https://www.cnblogs.com/zhili/p/DecoratorPattern.html
参考文档3:https://www.cnblogs.com/jajian/p/9729989.html(汽车例子)
参考文档4(次重点):https://blog.csdn.net/qian520ao/article/details/82529890(美颜例子,可以不错)
参考文档5(次重点):https://www.cnblogs.com/rookieFly-tdiitd/p/4914593.html(豆浆加价例子)
参考文档5(主重点):http://www.cnblogs.com/java-my-life/archive/2012/04/20/2455726.html(设计模式在JAVA I/O库中的应用)