模式定义

装饰器模式是一种结构型设计模式,它允许动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。装饰器模式通过创建一个包装对象来包裹真实的对象。

模式结构

graph TD A[Client] --> B((Component)) B --> C[ConcreteComponent] B --> D[Decorator] D --> E[ConcreteDecorator]

实现方式

基础实现

定义组件接口,实现具体组件和装饰器类。

// 组件接口
public interface Coffee {
    double getCost();
    String getDescription();
}

// 具体组件 - 简单咖啡
public class SimpleCoffee implements Coffee {
    @Override
    public double getCost() {
        return 2.0;
    }
    
    @Override
    public String getDescription() {
        return "简单咖啡";
    }
}

// 装饰器抽象类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
    
    @Override
    public double getCost() {
        return coffee.getCost();
    }
    
    @Override
    public String getDescription() {
        return coffee.getDescription();
    }
}

// 具体装饰器 - 牛奶
public class Milk extends CoffeeDecorator {
    public Milk(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + ", 牛奶";
    }
}

// 具体装饰器 - 糖
public class Sugar extends CoffeeDecorator {
    public Sugar(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + ", 糖";
    }
}

// 具体装饰器 - 奶油
public class Whip extends CoffeeDecorator {
    public Whip(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public double getCost() {
        return super.getCost() + 0.7;
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + ", 奶油";
    }
}

使用示例

public class CoffeeShop {
    public static void main(String[] args) {
        // 简单咖啡
        Coffee coffee = new SimpleCoffee();
        System.out.println("描述: " + coffee.getDescription());
        System.out.println("价格: $" + coffee.getCost());
        
        // 加牛奶的咖啡
        coffee = new Milk(coffee);
        System.out.println("描述: " + coffee.getDescription());
        System.out.println("价格: $" + coffee.getCost());
        
        // 加糖的咖啡
        coffee = new Sugar(coffee);
        System.out.println("描述: " + coffee.getDescription());
        System.out.println("价格: $" + coffee.getCost());
        
        // 加奶油的咖啡
        coffee = new Whip(coffee);
        System.out.println("描述: " + coffee.getDescription());
        System.out.println("价格: $" + coffee.getCost());
        
        // 创建复杂的咖啡
        Coffee specialCoffee = new Whip(new Sugar(new Milk(new SimpleCoffee())));
        System.out.println("\n特制咖啡:");
        System.out.println("描述: " + specialCoffee.getDescription());
        System.out.println("价格: $" + specialCoffee.getCost());
    }
}

文本处理示例

使用装饰器模式处理文本格式化。

// 文本组件接口
public interface Text {
    String getContent();
}

// 具体文本组件
public class PlainText implements Text {
    private String content;
    
    public PlainText(String content) {
        this.content = content;
    }
    
    @Override
    public String getContent() {
        return content;
    }
}

// 文本装饰器抽象类
public abstract class TextDecorator implements Text {
    protected Text text;
    
    public TextDecorator(Text text) {
        this.text = text;
    }
    
    @Override
    public String getContent() {
        return text.getContent();
    }
}

// 具体装饰器 - 粗体
public class BoldText extends TextDecorator {
    public BoldText(Text text) {
        super(text);
    }
    
    @Override
    public String getContent() {
        return "" + super.getContent() + "";
    }
}

// 具体装饰器 - 斜体
public class ItalicText extends TextDecorator {
    public ItalicText(Text text) {
        super(text);
    }
    
    @Override
    public String getContent() {
        return "" + super.getContent() + "";
    }
}

// 具体装饰器 - 下划线
public class UnderlineText extends TextDecorator {
    public UnderlineText(Text text) {
        super(text);
    }
    
    @Override
    public String getContent() {
        return "" + super.getContent() + "";
    }
}

// 客户端使用
public class TextEditor {
    public static void main(String[] args) {
        Text text = new PlainText("Hello World");
        System.out.println("原文: " + text.getContent());
        
        // 添加粗体
        text = new BoldText(text);
        System.out.println("粗体: " + text.getContent());
        
        // 添加斜体
        text = new ItalicText(text);
        System.out.println("粗体+斜体: " + text.getContent());
        
        // 添加下划线
        text = new UnderlineText(text);
        System.out.println("粗体+斜体+下划线: " + text.getContent());
        
        // 创建复杂格式
        Text complexText = new UnderlineText(new ItalicText(new BoldText(new PlainText("装饰器模式"))));
        System.out.println("\n复杂格式: " + complexText.getContent());
    }
}

经典案例

Java I/O 流

Java I/O 流库是装饰器模式的经典应用。

// Java I/O 流中的装饰器模式示例
import java.io.*;

public class IOStreamExample {
    public static void main(String[] args) {
        try {
            // FileInputStream 是具体组件
            FileInputStream fis = new FileInputStream("input.txt");
            
            // BufferedInputStream 是装饰器,添加缓冲功能
            BufferedInputStream bis = new BufferedInputStream(fis);
            
            // DataInputStream 是装饰器,添加读取基本数据类型的功能
            DataInputStream dis = new DataInputStream(bis);
            
            // 可以继续添加更多装饰器
            // 例如LineNumberInputStream(已废弃)可以添加行号功能
            
            // 读取数据
            int data = dis.read();
            while (data != -1) {
                System.out.print((char) data);
                data = dis.read();
            }
            
            dis.close();
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// 类结构说明:
// InputStream (Component)
// FileInputStream, ByteArrayInputStream (ConcreteComponent)
// FilterInputStream (Decorator)
// BufferedInputStream, DataInputStream (ConcreteDecorator)

Swing GUI 组件

Swing 中的 GUI 组件也使用了装饰器模式。

// Swing 中的装饰器模式示例
import javax.swing.*;
import java.awt.*;

public class SwingDecoratorExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("装饰器模式示例");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        
        // JLabel 是具体组件
        JLabel label = new JLabel("Hello World");
        
        // 可以通过设置属性来"装饰"组件
        label.setFont(new Font("Arial", Font.BOLD, 16));
        label.setForeground(Color.BLUE);
        label.setBorder(BorderFactory.createEtchedBorder());
        
        // JPanel 是容器组件
        JPanel panel = new JPanel();
        panel.add(label);
        
        frame.add(panel);
        frame.setVisible(true);
    }
}

// 更复杂的装饰示例
import javax.swing.border.*;
import java.awt.*;

public class ComplexSwingDecorator {
    public static void main(String[] args) {
        JFrame frame = new JFrame("复杂装饰示例");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        
        JButton button = new JButton("点击我");
        
        // 多层装饰
        // 1. 设置字体
        button.setFont(new Font("Arial", Font.BOLD, 14));
        
        // 2. 设置背景和前景色
        button.setBackground(Color.LIGHT_GRAY);
        button.setForeground(Color.BLUE);
        
        // 3. 添加边框装饰
        Border innerBorder = BorderFactory.createLineBorder(Color.BLUE, 2);
        Border outerBorder = BorderFactory.createEmptyBorder(10, 10, 10, 10);
        button.setBorder(BorderFactory.createCompoundBorder(outerBorder, innerBorder));
        
        frame.add(button);
        frame.setVisible(true);
    }
}

适用场景

优缺点

优点

  • 继承的替代方案:装饰器模式提供了一种比继承更灵活的替代方案,可以动态地扩展对象功能
  • 符合开闭原则:装饰器模式符合开闭原则,可以在不修改现有代码的情况下扩展对象功能
  • 功能组合灵活:可以通过不同的装饰器组合来实现不同的功能,非常灵活
  • 比继承更灵活:避免了大量子类的产生
  • 可以动态地扩展对象功能:可以在运行时添加或删除功能

缺点

  • 产生很多小对象:使用装饰器模式会产生很多小对象,增加系统的复杂度
  • 调试困难:由于装饰器模式会产生很多小对象,可能会增加调试的难度
  • 多次装饰的对象需要多次包装:多次装饰的对象需要逐级包装,可能会降低性能
  • 容易出错:使用装饰器模式时容易出错,对于多次装饰的对象,调试时寻找错误可能比较困难