【Java教程】Day20-09 设计模式:结构型模式——装饰器

1. 设计模式概述

在面向对象的编程中,装饰器模式(Decorator Pattern)是一种结构型设计模式,允许动态地为对象添加功能。这种模式比继承更为灵活,因为它能够在运行时进行功能扩展,而不需要创建大量的子类。

装饰器模式通过将功能封装到装饰器类中,能够使得对象在运行时不断地增加新的职责,而不会改变原始对象的结构。这个设计理念在很多实际的应用中都非常有效,尤其是当我们面对需要增加多种功能的情况时。

2. 装饰器模式的应用

2.1 装饰器模式与IO流

在Java标准库中,IO流的实现本身就遵循了装饰器模式。InputStream 是一个抽象类,它的多个子类(如 FileInputStreamServletInputStreamSocket.getInputStream())负责提供数据源。如果我们需要对这些数据源添加新的功能(如缓冲、加密、解压等),传统的做法是通过继承来创建多个子类。然而,当数据源或功能的种类增加时,子类数量会迅速暴涨,维护起来非常困难。

装饰器模式解决了这一问题。我们可以通过将不同的装饰器逐层包装在数据源对象周围,实现所需的附加功能,而不需要修改原始类。

2.2 示例:文件流与装饰器模式

假设我们有一个 FileInputStream,并且希望为它动态地增加缓冲和解压缩功能。在不使用装饰器模式的情况下,我们可能需要为每一种数据源和每一种功能创建多个子类。但使用装饰器模式,我们只需创建装饰器类,逐步为原始流添加功能。

以下是装饰器模式的代码示例:

java// 创建原始的数据源:InputStream fis = new FileInputStream("test.gz");// 增加缓冲功能:InputStream bis = new BufferedInputStream(fis);// 增加解压缩功能:InputStream gis = new GZIPInputStream(bis);

 

或者我们可以把它写成一行:

javaInputStream input = new GZIPInputStream( // 第二层装饰                        new BufferedInputStream( // 第一层装饰                            new FileInputStream("test.gz") // 核心功能                        ));

 

在这里,BufferedInputStream 和 GZIPInputStream 都是从 FilterInputStream 类继承的,而 FilterInputStream 充当了装饰器的角色。通过逐层包装,我们可以为流对象添加多个功能,而无需修改原始的数据源类。

2.3 装饰器模式的类图

装饰器模式的类图如下所示:

markdown             ┌───────────┐             │ Component │             └───────────┘      ┌────────────┼─────────────────┐      │            │                 │┌───────────┐┌───────────┐     ┌───────────┐│ComponentA ││ComponentB │...  │ Decorator │└───────────┘└───────────┘     └───────────┘                              ┌──────┴──────┐                              │             │                        ┌───────────┐ ┌───────────┐                        │DecoratorA │ │DecoratorB │...                        └───────────┘ └───────────┘

 

  • Component 是顶层接口或抽象类,对应于IO流中的 InputStream

  • ComponentA 和 ComponentB 是具体的数据源类,对应 FileInputStreamServletInputStream 等。

  • Decorator 是抽象装饰器类,对应于 FilterInputStream

  • 从 Decorator 派生出的具体装饰器类,如 BufferedInputStream 和 GZIPInputStream,提供具体的功能扩展。

3. 自定义装饰器模式:HTML文本渲染

接下来,我们来看一个通过装饰器模式实现文本效果的例子。假设我们需要动态地为HTML文本添加加粗、斜体、下划线等效果。我们可以通过定义一个顶层接口 TextNode 和多个装饰器类来实现这个需求。

3.1 定义顶层接口

首先,定义一个顶层接口 TextNode,用于设置和获取文本内容:

javapublic interface TextNode {    void setText(String text);   // 设置文本内容    String getText();            // 获取文本内容}

 

3.2 实现核心节点:SpanNode

SpanNode 作为一个简单的文本节点,实现了 TextNode 接口,返回基本的HTML标签 

javapublic class SpanNode implements TextNode {    private String text;    public void setText(String text) {        this.text = text;    }    public String getText() {        return "" + text + "";    }}

 

3.3 定义抽象装饰器类:NodeDecorator

接下来,我们定义一个抽象装饰器类 NodeDecorator,它实现了 TextNode 接口并持有一个 TextNode 实例,所有的具体装饰器都会从这个类继承:

javapublic abstract class NodeDecorator implements TextNode {    protected final TextNode target;    protected NodeDecorator(TextNode target) {        this.target = target;    }    public void setText(String text) {        this.target.setText(text);    }}

 

3.4 实现具体装饰器类:BoldDecorator

我们可以创建一个加粗装饰器 BoldDecorator,它会将原始文本内容包裹在  标签中:

javapublic class BoldDecorator extends NodeDecorator {    public BoldDecorator(TextNode target) {        super(target);    }    public String getText() {        return "" + target.getText() + "";    }}

 

3.5 客户端使用装饰器

客户端可以自由组合不同的装饰器,动态地为文本添加各种效果:

javaTextNode n1 = new SpanNode();TextNode n2 = new BoldDecorator(new UnderlineDecorator(new SpanNode()));TextNode n3 = new ItalicDecorator(new BoldDecorator(new SpanNode()));n1.setText("Hello");n2.setText("Decorated");n3.setText("World");System.out.println(n1.getText()); // 输出 HelloSystem.out.println(n2.getText()); // 输出 DecoratedSystem.out.println(n3.getText()); // 输出 World

 

3.6 练习

请实现一个装饰器,给文本添加  标签表示删除效果。

下载练习:[下载链接]

4. 小结

装饰器模式是一种非常灵活且强大的设计模式。它可以独立地增加核心功能和附加功能,并且允许我们在运行时动态地组合功能。与传统的继承方式相比,装饰器模式避免了子类数量爆炸的问题,使得系统更加灵活、可扩展。

通过装饰器模式,我们可以清晰地分离核心功能和附加功能,使得系统的功能扩展更加简单,并且能够根据需要灵活组合各种功能,极大地提高了代码的可维护性和可扩展性。

 

 

你可能感兴趣的:(Java教程,java,设计模式,python)