1. 概念
- 装饰者模式是一种结构型设计模式,它允许用户在不修改原始对象的基础上,通过将对象包装在装饰者类的对象中,动态地给原始对象添加新的行为或职责。
2. 原理结构图
图1
图2
- 抽象组件(Component):定义了对象的接口,可以给这些对象动态地增加职责。
- 具体组件(ConcreteComponent):是实现抽象组件接口的具体对象,它是被装饰器装饰的原始对象。
- 抽象装饰器(Decorator):是所有装饰器的抽象父类,它实现了抽象组件的接口,并持有一个抽象组件的引用。
- 具体装饰器(ConcreteDecorator):是具体的装饰器类,它实现了向被装饰对象添加功能的方法。
- 注意:如图2,如果ConcreteComponent类很多,还可以在Component与ConcreteComponent之间设计一个缓冲层,将共有的部分提取出来,抽象层一个类CommonConcreteComponent
3. 代码示例
3.1 示例1
- 在这个例子中,有一个Component接口,一个实现了该接口的ConcreteComponent类,以及两个实现了Decorator接口的装饰者类ConcreteDecoratorA和ConcreteDecoratorB。
java
// Component 接口
interface Component {
void operation();
}
// ConcreteComponent 类,实现了 Component 接口
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// Decorator 接口,继承了 Component 接口
interface Decorator extends Component {
void addedState();
}
// ConcreteDecoratorA 类,实现了 Decorator 接口,并持有一个 Component 对象的引用
class ConcreteDecoratorA implements Decorator {
private Component component;
public ConcreteDecoratorA(Component component) {
this.component = component;
}
@Override
public void operation() {
// 调用 Component 对象的 operation() 方法
component.operation();
// 添加新的功能或行为
addedState();
}
@Override
public void addedState() {
System.out.println("ConcreteDecoratorA addedState");
}
}
// ConcreteDecoratorB 类,同样实现了 Decorator 接口,并持有一个 Component 对象的引用
class ConcreteDecoratorB extends ConcreteDecoratorA {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void addedState() {
// 调用父类的 addedState() 方法
super.addedState();
// 添加新的功能或行为
System.out.println("ConcreteDecoratorB addedState");
}
}
// 测试类
public class DecoratorPatternDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
// 使用 ConcreteDecoratorA 装饰 component
ConcreteDecoratorA decoratorA = new ConcreteDecoratorA(component);
decoratorA.operation();
// 使用 ConcreteDecoratorB 装饰 component,这里其实是通过 ConcreteDecoratorA 进行装饰的,形成链式装饰
ConcreteDecoratorB decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.operation();
}
}
- 在这个示例中,ConcreteComponent类有一个operation()方法,该方法执行一些基本操作。ConcreteDecoratorA和ConcreteDecoratorB类都扩展了这个功能,通过在operation()方法中添加额外的行为来实现。ConcreteDecoratorB还通过继承ConcreteDecoratorA来组合多个装饰者的功能。
- 运行DecoratorPatternDemo类的main方法,你将看到如下输出:
java
ConcreteComponent operation
ConcreteDecoratorA addedState
ConcreteComponent operation
ConcreteDecoratorA addedState
ConcreteDecoratorB addedState
3.2 示例2
- 具体案例:展示如何使用装饰者模式为一个文本编辑器(TextEditor)添加不同的功能,如字体加粗(BoldDecorator)、字体斜体(ItalicDecorator)和下划线(UnderlineDecorator)。
java
// Component 接口
interface TextEditorComponent {
String writeText(String text);
}
// ConcreteComponent 类,实现 TextEditorComponent 接口
class SimpleTextEditor implements TextEditorComponent {
@Override
public String writeText(String text) {
return "Simple Text: " + text;
}
}
// Decorator 抽象类
abstract class TextEditorDecorator implements TextEditorComponent {
protected TextEditorComponent textEditor;
public TextEditorDecorator(TextEditorComponent textEditor) {
this.textEditor = textEditor;
}
@Override
public String writeText(String text) {
return textEditor.writeText(text);
}
}
// BoldDecorator 类,添加字体加粗功能
class BoldDecorator extends TextEditorDecorator {
public BoldDecorator(TextEditorComponent textEditor) {
super(textEditor);
}
@Override
public String writeText(String text) {
return "<b>" + super.writeText(text) + "</b>";
}
}
// ItalicDecorator 类,添加字体斜体功能
class ItalicDecorator extends TextEditorDecorator {
public ItalicDecorator(TextEditorComponent textEditor) {
super(textEditor);
}
@Override
public String writeText(String text) {
return "<i>" + super.writeText(text) + "</i>";
}
}
// UnderlineDecorator 类,添加下划线功能
class UnderlineDecorator extends TextEditorDecorator {
public UnderlineDecorator(TextEditorComponent textEditor) {
super(textEditor);
}
@Override
public String writeText(String text) {
return "<u>" + super.writeText(text) + "</u>";
}
}
// 测试类
public class TextEditorPatternDemo {
public static void main(String[] args) {
// 创建一个简单的文本编辑器对象
TextEditorComponent simpleEditor = new SimpleTextEditor();
// 使用装饰者来增强文本编辑器的功能
TextEditorComponent boldEditor = new BoldDecorator(simpleEditor);
TextEditorComponent italicEditor = new ItalicDecorator(boldEditor);
TextEditorComponent underlineEditor = new UnderlineDecorator(italicEditor);
// 使用增强后的文本编辑器写入文本
String text = "Hello, Decorator Pattern!";
String decoratedText = underlineEditor.writeText(text);
// 输出结果
System.out.println(decoratedText);
}
}
- 运行 TextEditorPatternDemo 类的main方法,你将看到如下输出:
java
<u><i><b>Simple Text: Hello, Decorator Pattern!</b></i></u>
- 在这个例子中,创建了一个简单的文本编辑器,并通过装饰者链动态地为其添加了字体加粗、斜体和下划线功能。装饰者模式允许根据需要在运行时组合不同的装饰者,从而灵活地增强对象的功能。这种灵活性使得装饰者模式在处理复杂场景时非常有用。
4. 优缺点
- 主要作用
- 动态地增强对象的功能,通过组合不同的装饰者实现功能的灵活扩展,同时保持代码的稳定性和可维护性,且对客户端透明。
- 优点
- 扩展灵活:装饰者模式提供了比继承更加灵活的扩展方式。通过装饰者模式,可以在不改变原有类的基础上,为对象动态添加额外的功能。
- 即插即用:装饰者模式允许用户按需添加功能,这些功能可以即时生效,不需要对原有系统进行大量修改。
- 组合灵活:可以通过不同的装饰者类及其排列组合,实现多种不同的功能扩展,这样可以灵活地满足不同的需求场景。
- 遵守开闭原则:装饰者模式遵循了软件设计的开闭原则,即对扩展开放,对修改封闭。这意味着系统可以很容易地进行扩展,而不需要修改现有的代码。
- 缺点
- 复杂性增加:虽然装饰者模式提供了灵活性,但同时也可能增加系统的复杂性。因为需要理解如何将多个装饰者组合在一起,以及它们之间的交互方式。
- 设计难度:正确实现装饰者模式可能需要更细致的设计和规划,尤其是在确定哪些功能应该由哪个装饰者负责时。
- 调试困难:由于装饰者模式在运行时动态添加功能,这可能会使得问题的定位和调试变得更加困难。
- 性能考虑:频繁地使用装饰者模式可能会导致性能问题,特别是在装饰者数量较多或者装饰逻辑复杂时。
5. 应用场景
5.1 主要包括以下几个方面
- 动态扩展功能:当需要动态地给一个对象增加一些额外的职责或功能时,装饰者模式非常适用。这些功能可以在运行时根据需要添加或撤销,提供了比继承更有弹性的替代方案。
- 避免子类爆炸:在使用继承来实现功能扩展时,随着功能的增多,子类可能会变得非常庞大和复杂,导致所谓的"子类爆炸"问题。装饰者模式通过在不改变原有对象结构的基础上添加功能,有效地避免了这个问题。
- 灵活组合功能:当需要组合多个功能或职责时,装饰者模式允许将不同的装饰者对象链接在一起,形成一个功能强大的对象。这种组合方式可以产生各种复杂的功能组合,以满足不同的需求。
- 透明的功能增强:装饰者模式允许对原始对象进行透明的包装和增强,即客户端可以像使用原始对象一样使用装饰后的对象,而无需关心其内部实现细节。这使得装饰者模式在保持原有接口不变的情况下,为对象增加了新的功能。
5.2 实际应用
- 用户界面定制
- 实例:在开发一个图形用户界面(GUI)应用程序时,可以使用装饰者模式为按钮、文本框等组件添加不同的样式或行为。比如,可以给一个按钮添加不同的背景色、字体或点击事件处理器,而无需修改按钮本身的代码。
- 权限控制
- 实例:在一个系统中,可以使用装饰者模式为特定的操作添加权限控制。例如,可以为一个用户管理类添加一个装饰者,用于检查用户是否具有执行特定操作的权限。
- 文件处理
- 实例:在处理文件时,可能需要为文件添加不同的处理方式,如加密、压缩等。可以使用装饰者模式来动态地为文件对象添加这些功能。每个装饰者都可以负责一种特定的处理方式,通过组合多个装饰者,可以实现复杂的文件处理流程。
- 手机配件
- 实例:在手机的使用过程中,用户可以通过添加各种配件来增强手机的功能,如手机壳、钢化膜、耳机等。这些配件可以看作是手机的装饰者,它们为手机增加了额外的保护、美观或音频输出等功能。
- 电影特效
- 实例:在电影制作中,特效团队会为影片添加各种视觉效果,如烟雾、爆炸、光影等。这些特效可以看作是影片的装饰者,它们为影片增添了视觉冲击力和观赏性。
- ...
6. JDK中的使用
- Java I/O中的流
- InputStream 是一个抽象的组件接口,而 FileInputStream 是它的一个具体实现。为了提供额外的功能,如缓冲或过滤,可以使用装饰者模式来动态地添加这些功能,而不是修改 FileInputStream 类本身。这样的设计使得I/O流非常灵活,可以通过组合不同的装饰者来实现复杂的功能。
- InputStream 是一个抽象的组件接口,而 FileInputStream 是它的一个具体实现。为了提供额外的功能,如缓冲或过滤,可以使用装饰者模式来动态地添加这些功能,而不是修改 FileInputStream 类本身。这样的设计使得I/O流非常灵活,可以通过组合不同的装饰者来实现复杂的功能。
- InputStream和其子类:InputStream 是装饰者模式中的抽象组件(Component)。FileInputStream、ByteArrayInputStream、ObjectInputStream 等直接继承自 InputStream 的类相当于装饰者模式中的具体组件(ConcreteComponent)。
- BufferedReader和其他装饰类:BufferedReader 是 Reader 的一个装饰类,它增加了缓冲区来提高字符读取的效率。同样,BufferedWriter 对 Writer 也有类似的装饰作用。
- FilterInputStream:这个类是 InputStream 的装饰者,它提供了一些额外的功能,比如可以添加其他输入流的装饰者。
7. 注意事项
- 保持接口的一致性:装饰者和被装饰对象应该具有相同的接口,这样客户端代码就可以透明地使用它们,无需关心具体是装饰者还是被装饰对象。这有助于保持代码的简洁性和一致性。
- 避免装饰者之间的依赖:装饰者之间应该保持独立,避免产生复杂的依赖关系。这样可以确保装饰者可以单独使用或组合使用,而不会引入额外的复杂性。
- 装饰者的顺序:当使用多个装饰者时,它们的顺序可能会影响最终的结果。因此,在设计时需要考虑装饰者的顺序问题,并确保它们能够正确地组合在一起。
- 角色定义:明确定义不同的角色,包括抽象组件(Component)、具体组件(ConcreteComponent)和装饰者(Decorator),以确保每个部分的职责清晰。
- 避免过度设计:虽然装饰者模式提供了强大的功能扩展能力,但不应过度使用,以免引入不必要的复杂性和性能开销。
- 适用场景评估:在考虑使用装饰者模式之前,应评估是否真的需要动态添加功能,以及这些功能是否可以在不影响现有系统的情况下撤销或修改。
8. 生成器模式 VS 组合模式 VS 装饰器模式
模式 | 目的 | 模式架构主要角色 | 应用场景 |
---|---|---|---|
建造者模式 | 分步构建复杂对象 | 指挥者,生成器 | 构建具有复杂逻辑的对象 |
组合模式 | 表示具有层次结构的对象 | 组合类和叶子节点 | 树形结构和递归结构 |
装饰器模式 | 动态添加新功能 | 抽象组件和装饰器 | 功能组合和扩展 |