简介
从简介不难看出,装饰器模式主要的作用在于扩充原来类的功能。就好像HTML可以用来展示数据,CSS可以在展示数据的基础上,对数据进行美化。
一般我们想要扩充一个类的功能,可以使用继承的方式,也可以使用装饰器模式。下面我们分别使用两种方式来模拟HTML和CSS的关系。
代码示例
需求
新建一个接口,表示展示文字的功能
public interface IDisplayText {
void displayText();
}
新建一个Span类,实现该接口,用来展示以一段文字
public class Span implements IDisplayText {
@Override
public void displayText() {
System.out.println("this is a span");
}
}
现在我们想要扩充Span类的功能,例如想要实现红色字体的效果。
使用继承实现
新建一个Span类的子类,重写displayText方法
public class RedSpan extends Span {
@Override
public void displayText() {
System.out.println("style = 'color:red;'");
super.displayText();
}
}
这个时候,我们就可以使用装饰之后的span了
public class Document {
public static void main(String[] args) {
IDisplayText span = new Span();
span.displayText();
System.out.println("==========装饰之后=========");
span = new RedSpan();
span.displayText();
}
}
运行结果:
this is a span
==========装饰之后=========
style = 'color:red;'
this is a span
但是,继承的缺点显而易见,由于Java单继承的特性,当我们继承了要扩展的类之后就不能继承其他的类,而且代码的耦合度比较高,这个时候使用装饰者模式是一个较好的选择。
使用装饰者模式
新建一个装饰者抽象类
public abstract class DisplayTextWrapper implements IDisplayText {
private IDisplayText span;
public DisplayTextWrapper(IDisplayText span) {
this.span = span;
}
@Override
public void displayText() {
span.displayText();
}
}
新建装饰类继承抽象类
public class RedDisplayTextWrapper extends DisplayTextWrapper {
public RedDisplayTextWrapper(IDisplayText span) {
super(span);
}
@Override
public void displayText() {
System.out.println("style = 'color:red;'");
super.displayText();
}
}
这个时候就可以使用装饰类对span进行装饰
public class Document {
public static void main(String[] args) {
IDisplayText span = new Span();
span.displayText();
System.out.println("==========装饰之后=========");
span = new RedDisplayTextWrapper(span);
span.displayText();
}
}
运行结果:
this is a span
==========装饰之后=========
style = 'color:red;'
this is a span
UML类图:
总结
优点
与继承相比,装饰者模式的灵活性比较高,就上面的RedDisplayTextWrapper
装饰类而言,它可以装饰任意一个实现IDisplayText
接口的类,而继承只能扩充Span
的功能。
装饰模式是动态的给类增加功能,而继承是静态的给类增加功能。同时,装饰模式可以替代继承,解决我们类膨胀的问题。
缺点
装饰者模式的过度使用会产生越来越多的装饰类,使程序变得复杂。
应用场景
- 对原有类功能的增强,例如 Java IO 操作中FileInputStream和BufferedInputStream
- 动态的给一个类添加功能,这个功能以后可能会被删除
- 项目需求变更,需要在原有功能的基础之上升级,但是又不希望改动原有的类