设计模式-装饰者模式(Decorator)

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 主要包括以下几个方面
  1. 动态扩展功能:当需要动态地给一个对象增加一些额外的职责或功能时,装饰者模式非常适用。这些功能可以在运行时根据需要添加或撤销,提供了比继承更有弹性的替代方案。
  2. 避免子类爆炸:在使用继承来实现功能扩展时,随着功能的增多,子类可能会变得非常庞大和复杂,导致所谓的"子类爆炸"问题。装饰者模式通过在不改变原有对象结构的基础上添加功能,有效地避免了这个问题。
  3. 灵活组合功能:当需要组合多个功能或职责时,装饰者模式允许将不同的装饰者对象链接在一起,形成一个功能强大的对象。这种组合方式可以产生各种复杂的功能组合,以满足不同的需求。
  4. 透明的功能增强:装饰者模式允许对原始对象进行透明的包装和增强,即客户端可以像使用原始对象一样使用装饰后的对象,而无需关心其内部实现细节。这使得装饰者模式在保持原有接口不变的情况下,为对象增加了新的功能。

5.2 实际应用
  1. 用户界面定制
    • 实例:在开发一个图形用户界面(GUI)应用程序时,可以使用装饰者模式为按钮、文本框等组件添加不同的样式或行为。比如,可以给一个按钮添加不同的背景色、字体或点击事件处理器,而无需修改按钮本身的代码。
  2. 权限控制
    • 实例:在一个系统中,可以使用装饰者模式为特定的操作添加权限控制。例如,可以为一个用户管理类添加一个装饰者,用于检查用户是否具有执行特定操作的权限。
  3. 文件处理
    • 实例:在处理文件时,可能需要为文件添加不同的处理方式,如加密、压缩等。可以使用装饰者模式来动态地为文件对象添加这些功能。每个装饰者都可以负责一种特定的处理方式,通过组合多个装饰者,可以实现复杂的文件处理流程。
  4. 手机配件
    • 实例:在手机的使用过程中,用户可以通过添加各种配件来增强手机的功能,如手机壳、钢化膜、耳机等。这些配件可以看作是手机的装饰者,它们为手机增加了额外的保护、美观或音频输出等功能。
  5. 电影特效
    • 实例:在电影制作中,特效团队会为影片添加各种视觉效果,如烟雾、爆炸、光影等。这些特效可以看作是影片的装饰者,它们为影片增添了视觉冲击力和观赏性。
  6. ...

6. JDK中的使用

  • Java 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 装饰器模式

模式 目的 模式架构主要角色 应用场景
建造者模式 分步构建复杂对象 指挥者,生成器 构建具有复杂逻辑的对象
组合模式 表示具有层次结构的对象 组合类和叶子节点 树形结构和递归结构
装饰器模式 动态添加新功能 抽象组件和装饰器 功能组合和扩展
相关推荐
CyreneSimon24 分钟前
使用 LoRA 进行模型微调的步骤
python·transformer
ymchuangke26 分钟前
数据清洗-缺失值处理-缺失值可视化图(竖线)
python·算法·数学建模
薛文旺1 小时前
c++可视化打印树
开发语言·c++
计算机学姐1 小时前
基于python+django+vue的旅游网站系统
开发语言·vue.js·python·mysql·django·旅游·web3.py
qq_278063711 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
Beginner_bml1 小时前
结构体---C语言
c语言·开发语言·数据结构
程序员小羊!1 小时前
Python语言基础教程(下)4.0
开发语言·python
huanxiangcoco1 小时前
73. 矩阵置零
python·leetcode·矩阵
vczxh1 小时前
c++ templates常用函数
开发语言·c++
喝旺仔la2 小时前
VSCode的使用
java·开发语言·javascript