装饰器模式
- 专栏目录
- 装饰器模式的意图,解决的问题,什么时候使用
- 1.装饰器模式
- 2.装饰器模式中的角色
- 2.1 透明装饰模式
- 2.2 半透明装饰模式
- 2.3 需要注意的问题
- 3.装饰器模式优缺点、使用场景
- 优点:
- 缺点:
- 适用场景:
装饰器模式的意图,解决的问题,什么时候使用
有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。这里的例子总结一下:
- 早餐对象:加鸡蛋,加牛奶…
- 房子对象:装修门框,装修墙壁,装修窗户…
在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。以上例子的行为该怎么通过面向对象变成语言体现呢?下面请看装饰器模式。
1.装饰器模式
装饰器(Decorator)模式,指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标。
2.装饰器模式中的角色
组成(角色) | 作用 |
抽象构件(Component)角色 | 定义一个抽象接口以规范准备接收附加责任的对象。 |
具体构件(ConcreteComponent)角色 | 实现抽象构件,通过装饰角色为其添加一些职责。(当只有一个具体构件时可以省略此角色) |
抽象装饰(Decorator)角色 | 继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。(当只有一个具体构件时,此对象同时充当具体构件对象,并且不再是抽象类) |
具体装饰(ConcreteDecorator)角色 | 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。 |
多个具体构件对象的结构图:
确定系统只有一个具体构件对象时的结构图:
装饰模式分为两种:1.透明装饰模式 2.半透明装饰模式
他们的区别在于:是否使客户端只面向抽象类编程。
2.1 透明装饰模式
标准程序代码如下:
package decorator;
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
}
//抽象构件角色
interface Component {
public void operation();
}
//具体构件角色
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色
class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();//透明模式下,将具体装饰方法在抽象方法中一起调用,有点类似代理
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
运行结果:
创建具体构件角色
调用具体构件角色的方法operation()
---------------------------------
调用具体构件角色的方法operation()
为具体构件角色增加额外的功能addedFunction()
透明模式下客户端完全面向抽象类变成,可以对构件对象进行多次装饰
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator1(new ConcreteDecorator2(new ConcreteDecorator3(p)));
d.operation();
}
}
2.2 半透明装饰模式
半透明模式其实只在具体装饰类的实现和客户端的调用方式有区别
具体装饰类:
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
// addedFunction();//半透明模式下,将具体装饰方法不在抽象方法中一起调用,由客户但主动调用
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
客户端:
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
ConcreteDecorator d = new ConcreteDecorator(p);//声明成具体装饰类,手动调用装饰方法
d.operation();
d.addedFunction();//手动调用装饰方法
}
}
半透明装饰模式提供了一定的灵活性选择,但是带来的弊端是无法对一个构件对象进行多次装饰.(因为装饰方法没有在抽象方法中调用)
2.3 需要注意的问题
- 尽量保持装饰类的接口与被装饰类的接口相冋’这样’对于客户端而言’无论是装饰之前的对象还是装饰之后的对象都可以一致对待。这也就是说’在可能的情况下’我们应该尽量使用透明装饰模式.
- 尽量保持具体构件类 Concrete Component是一个“轻”类’也就是说不要把太多的行为放在具题构件类中,我们可以通过装饰类对其进行扩展
3.装饰器模式优缺点、使用场景
优点:
- 1.对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧増加。
- 2.符合开闭原则,可以动态指定具体装饰类(指定具体行为)
- 3.通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果(因为装饰是有顺序的)
缺点:
- 1.装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
- 2.对于多次装饰一个构件对象的情况下,系统变得复杂很难排错.
适用场景:
- 1.当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
- 2.当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
装饰器模式在 Java 语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。