设计模式之:装饰器模式

文章目录

什么是装饰器模式?

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

核心思想

装饰器模式的核心思想是:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

生活中的装饰器模式

想象一下我们去咖啡店点咖啡:

  • 首先点一杯基础咖啡(被装饰对象)
  • 然后可以添加牛奶(装饰器)
  • 再加糖(另一个装饰器)
  • 再加奶油(又一个装饰器)

每个装饰器都在基础咖啡上添加新的特性,但咖啡的本质没有改变。

模式结构

装饰器模式包含四个核心角色:

  1. 组件接口(Component):定义对象的接口,可以动态地给这些对象添加职责
  2. 具体组件(ConcreteComponent):定义具体的对象,可以给这个对象添加一些职责
  3. 装饰器抽象类(Decorator):继承组件接口,并包含一个组件对象的引用
  4. 具体装饰器(ConcreteDecorator):向组件添加具体的职责

基础示例:咖啡店系统

1. 组件接口

java 复制代码
/**
 * 饮料接口 - 组件接口
 * 定义所有饮料的共同操作
 */
public interface Beverage {
    /**
     * 获取饮料描述
     */
    String getDescription();
    
    /**
     * 计算成本
     */
    double cost();
    
    /**
     * 获取完整信息
     */
    default String getFullInfo() {
        return String.format("%s - ¥%.2f", getDescription(), cost());
    }
}

2. 具体组件

java 复制代码
/**
 * 浓缩咖啡 - 具体组件
 */
public class Espresso implements Beverage {
    @Override
    public String getDescription() {
        return "浓缩咖啡";
    }
    
    @Override
    public double cost() {
        return 25.0;
    }
}

/**
 * 美式咖啡 - 具体组件
 */
public class Americano implements Beverage {
    @Override
    public String getDescription() {
        return "美式咖啡";
    }
    
    @Override
    public double cost() {
        return 20.0;
    }
}

/**
 * 拿铁咖啡 - 具体组件
 */
public class Latte implements Beverage {
    @Override
    public String getDescription() {
        return "拿铁咖啡";
    }
    
    @Override
    public double cost() {
        return 30.0;
    }
}

3. 装饰器抽象类

java 复制代码
/**
 * 调料装饰器抽象类 - 装饰器
 * 所有具体装饰器的父类
 */
public abstract class CondimentDecorator implements Beverage {
    // 持有被装饰对象的引用
    protected Beverage beverage;
    
    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
    
    @Override
    public abstract String getDescription();
    
    @Override
    public double cost() {
        return beverage.cost();
    }
}

4. 具体装饰器

java 复制代码
/**
 * 牛奶装饰器
 */
public class MilkDecorator extends CondimentDecorator {
    public MilkDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 牛奶";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 5.0;
    }
}

/**
 * 糖装饰器
 */
public class SugarDecorator extends CondimentDecorator {
    public SugarDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 糖";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 2.0;
    }
}

/**
 * 奶油装饰器
 */
public class CreamDecorator extends CondimentDecorator {
    public CreamDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 奶油";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 8.0;
    }
}

/**
 * 香草糖浆装饰器
 */
public class VanillaSyrupDecorator extends CondimentDecorator {
    public VanillaSyrupDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 香草糖浆";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 6.0;
    }
}

/**
 * 巧克力装饰器
 */
public class ChocolateDecorator extends CondimentDecorator {
    public ChocolateDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 巧克力";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 7.0;
    }
}

5. 客户端使用

java 复制代码
/**
 * 咖啡店客户端
 */
public class CoffeeShop {
    public static void main(String[] args) {
        System.out.println("=== 咖啡店订单系统 ===\n");
        
        // 订单1:浓缩咖啡 + 牛奶 + 糖
        System.out.println("订单1:");
        Beverage order1 = new Espresso();          // 浓缩咖啡
        order1 = new MilkDecorator(order1);        // 加牛奶
        order1 = new SugarDecorator(order1);       // 加糖
        System.out.println(order1.getFullInfo());
        
        // 订单2:拿铁 + 奶油 + 巧克力 + 香草糖浆
        System.out.println("\n订单2:");
        Beverage order2 = new Latte();             // 拿铁
        order2 = new CreamDecorator(order2);       // 加奶油
        order2 = new ChocolateDecorator(order2);   // 加巧克力
        order2 = new VanillaSyrupDecorator(order2);// 加香草糖浆
        System.out.println(order2.getFullInfo());
        
        // 订单3:美式咖啡 + 牛奶
        System.out.println("\n订单3:");
        Beverage order3 = new Americano();         // 美式咖啡
        order3 = new MilkDecorator(order3);        // 加牛奶
        System.out.println(order3.getFullInfo());
        
        // 显示所有订单总价
        System.out.println("\n=== 订单汇总 ===");
        double total = order1.cost() + order2.cost() + order3.cost();
        System.out.printf("总金额: ¥%.2f%n", total);
    }
}

完整示例:文本处理系统

让我们通过一个更复杂的文本处理系统来深入理解装饰器模式。

1. 文本组件接口

java 复制代码
/**
 * 文本组件接口
 */
public interface TextComponent {
    /**
     * 获取文本内容
     */
    String getContent();
    
    /**
     * 获取文本长度
     */
    int getLength();
    
    /**
     * 显示文本信息
     */
    default void display() {
        System.out.println("内容: " + getContent());
        System.out.println("长度: " + getLength());
    }
}

2. 具体文本组件

java 复制代码
/**
 * 基础文本 - 具体组件
 */
public class PlainText implements TextComponent {
    private final String content;
    
    public PlainText(String content) {
        this.content = content;
    }
    
    @Override
    public String getContent() {
        return content;
    }
    
    @Override
    public int getLength() {
        return content.length();
    }
}

/**
 * 加密文本 - 具体组件
 */
public class EncryptedText implements TextComponent {
    private final String content;
    
    public EncryptedText(String content) {
        this.content = encrypt(content);
    }
    
    @Override
    public String getContent() {
        return decrypt(content);
    }
    
    @Override
    public int getLength() {
        return decrypt(content).length();
    }
    
    private String encrypt(String text) {
        // 简单的加密:字符偏移
        char[] chars = text.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] = (char) (chars[i] + 1);
        }
        return new String(chars);
    }
    
    private String decrypt(String encrypted) {
        // 解密:字符偏移还原
        char[] chars = encrypted.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] = (char) (chars[i] - 1);
        }
        return new String(chars);
    }
}

3. 文本装饰器抽象类

java 复制代码
/**
 * 文本装饰器抽象类
 */
public abstract class TextDecorator implements TextComponent {
    protected TextComponent textComponent;
    
    public TextDecorator(TextComponent textComponent) {
        this.textComponent = textComponent;
    }
    
    @Override
    public String getContent() {
        return textComponent.getContent();
    }
    
    @Override
    public int getLength() {
        return textComponent.getLength();
    }
}

4. 具体文本装饰器

java 复制代码
/**
 * HTML装饰器 - 给文本添加HTML标签
 */
public class HtmlDecorator extends TextDecorator {
    public HtmlDecorator(TextComponent textComponent) {
        super(textComponent);
    }
    
    @Override
    public String getContent() {
        return "<html><body>" + textComponent.getContent() + "</body></html>";
    }
    
    @Override
    public int getLength() {
        return getContent().length();
    }
    
    @Override
    public void display() {
        System.out.println("HTML格式:");
        System.out.println(getContent());
    }
}

/**
 * 大写装饰器 - 将文本转换为大写
 */
public class UpperCaseDecorator extends TextDecorator {
    public UpperCaseDecorator(TextComponent textComponent) {
        super(textComponent);
    }
    
    @Override
    public String getContent() {
        return textComponent.getContent().toUpperCase();
    }
}

/**
 * 小写装饰器 - 将文本转换为小写
 */
public class LowerCaseDecorator extends TextDecorator {
    public LowerCaseDecorator(TextComponent textComponent) {
        super(textComponent);
    }
    
    @Override
    public String getContent() {
        return textComponent.getContent().toLowerCase();
    }
}

/**
 * 压缩装饰器 - 移除多余空格
 */
public class CompressDecorator extends TextDecorator {
    public CompressDecorator(TextComponent textComponent) {
        super(textComponent);
    }
    
    @Override
    public String getContent() {
        return textComponent.getContent().replaceAll("\\s+", " ").trim();
    }
    
    @Override
    public int getLength() {
        return getContent().length();
    }
}

/**
 * 前缀装饰器 - 添加前缀
 */
public class PrefixDecorator extends TextDecorator {
    private final String prefix;
    
    public PrefixDecorator(TextComponent textComponent, String prefix) {
        super(textComponent);
        this.prefix = prefix;
    }
    
    @Override
    public String getContent() {
        return prefix + textComponent.getContent();
    }
    
    @Override
    public int getLength() {
        return getContent().length();
    }
}

/**
 * 后缀装饰器 - 添加后缀
 */
public class SuffixDecorator extends TextDecorator {
    private final String suffix;
    
    public SuffixDecorator(TextComponent textComponent, String suffix) {
        super(textComponent);
        this.suffix = suffix;
    }
    
    @Override
    public String getContent() {
        return textComponent.getContent() + suffix;
    }
    
    @Override
    public int getLength() {
        return getContent().length();
    }
}

/**
 * 颜色装饰器 - 添加颜色标记
 */
public class ColorDecorator extends TextDecorator {
    private final String color;
    
    public ColorDecorator(TextComponent textComponent, String color) {
        super(textComponent);
        this.color = color;
    }
    
    @Override
    public String getContent() {
        return String.format("[颜色:%s]%s[/颜色]", color, textComponent.getContent());
    }
    
    @Override
    public int getLength() {
        // 颜色标记不计入内容长度
        return textComponent.getLength();
    }
}

/**
 * 边框装饰器 - 给文本添加边框
 */
public class BorderDecorator extends TextDecorator {
    private final char borderChar;
    
    public BorderDecorator(TextComponent textComponent, char borderChar) {
        super(textComponent);
        this.borderChar = borderChar;
    }
    
    @Override
    public String getContent() {
        String content = textComponent.getContent();
        String borderLine = String.valueOf(borderChar).repeat(content.length() + 4);
        
        return borderLine + "\n" +
               borderChar + " " + content + " " + borderChar + "\n" +
               borderLine;
    }
    
    @Override
    public int getLength() {
        return getContent().length();
    }
}

5. 文本处理器客户端

java 复制代码
/**
 * 文本处理器客户端
 */
public class TextProcessor {
    public static void main(String[] args) {
        System.out.println("=== 文本处理系统 ===\n");
        
        // 示例1:基础文本处理
        demonstrateBasicTextProcessing();
        
        // 示例2:格式化文本
        demonstrateTextFormatting();
        
        // 示例3:加密文本处理
        demonstrateEncryptedText();
        
        // 示例4:复杂装饰组合
        demonstrateComplexDecoration();
    }
    
    /**
     * 演示基础文本处理
     */
    private static void demonstrateBasicTextProcessing() {
        System.out.println("1. 基础文本处理演示:");
        System.out.println("-".repeat(40));
        
        // 创建基础文本
        TextComponent text = new PlainText("   Hello,   Decorator  Pattern!   ");
        System.out.println("原始文本:");
        text.display();
        
        // 添加压缩装饰器
        text = new CompressDecorator(text);
        System.out.println("\n压缩后:");
        text.display();
        
        // 添加大写装饰器
        text = new UpperCaseDecorator(text);
        System.out.println("\n大写后:");
        text.display();
        
        // 添加前缀后缀
        text = new PrefixDecorator(text, ">>> ");
        text = new SuffixDecorator(text, " <<<");
        System.out.println("\n添加前后缀:");
        text.display();
    }
    
    /**
     * 演示文本格式化
     */
    private static void demonstrateTextFormatting() {
        System.out.println("\n2. 文本格式化演示:");
        System.out.println("-".repeat(40));
        
        TextComponent text = new PlainText("装饰器模式很实用");
        
        // 添加颜色和边框
        text = new ColorDecorator(text, "红色");
        text = new BorderDecorator(text, '*');
        
        System.out.println("格式化文本:");
        System.out.println(text.getContent());
    }
    
    /**
     * 演示加密文本处理
     */
    private static void demonstrateEncryptedText() {
        System.out.println("\n3. 加密文本处理演示:");
        System.out.println("-".repeat(40));
        
        // 创建加密文本
        TextComponent secretText = new EncryptedText("机密信息:项目预算100万");
        
        System.out.println("加密内容: " + secretText.getContent());
        System.out.println("解密后长度: " + secretText.getLength());
        
        // 对加密文本进行装饰
        secretText = new UpperCaseDecorator(secretText);
        secretText = new BorderDecorator(secretText, '#');
        
        System.out.println("\n装饰后的加密文本:");
        System.out.println(secretText.getContent());
    }
    
    /**
     * 演示复杂装饰组合
     */
    private static void demonstrateComplexDecoration() {
        System.out.println("\n4. 复杂装饰组合演示:");
        System.out.println("-".repeat(40));
        
        // 创建复杂的文本处理流水线
        TextComponent text = new PlainText("   Welcome to Java Design Patterns   ");
        
        System.out.println("原始文本:");
        text.display();
        
        // 构建处理流水线
        text = new CompressDecorator(text);        // 压缩空格
        text = new UpperCaseDecorator(text);       // 转大写
        text = new PrefixDecorator(text, "🚀 ");   // 添加前缀
        text = new SuffixDecorator(text, " 🎯");   // 添加后缀
        text = new ColorDecorator(text, "蓝色");   // 添加颜色
        text = new BorderDecorator(text, '═');     // 添加边框
        
        System.out.println("\n经过完整处理流水线:");
        System.out.println(text.getContent());
        System.out.println("最终长度: " + text.getLength());
    }
}

实际应用示例:IO流系统

Java的IO流系统是装饰器模式的经典应用:

java 复制代码
import java.io.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * Java IO流装饰器模式示例
 */
public class IOStreamExample {
    public static void main(String[] args) {
        System.out.println("=== Java IO流装饰器模式示例 ===\n");
        
        // 示例1:基础文件读写
        demonstrateBasicFileIO();
        
        // 示例2:缓冲流装饰器
        demonstrateBufferedStream();
        
        // 示例3:数据流装饰器
        demonstrateDataStream();
        
        // 示例4:压缩流装饰器
        demonstrateCompressionStream();
    }
    
    /**
     * 演示基础文件读写
     */
    private static void demonstrateBasicFileIO() {
        System.out.println("1. 基础文件IO:");
        
        try {
            // 文件输出流 - 具体组件
            FileOutputStream fos = new FileOutputStream("test.txt");
            
            // 使用装饰器添加功能
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            PrintStream ps = new PrintStream(bos);
            
            ps.println("Hello, Decorator Pattern!");
            ps.println("This is a test message.");
            
            ps.close();
            System.out.println("文件写入完成");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 演示缓冲流装饰器
     */
    private static void demonstrateBufferedStream() {
        System.out.println("\n2. 缓冲流装饰器:");
        
        try {
            // 读取文件:FileInputStream -> BufferedInputStream -> DataInputStream
            FileInputStream fis = new FileInputStream("test.txt");
            BufferedInputStream bis = new BufferedInputStream(fis);
            
            System.out.println("读取文件内容:");
            int data;
            while ((data = bis.read()) != -1) {
                System.out.print((char) data);
            }
            
            bis.close();
            System.out.println("\n文件读取完成");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 演示自定义装饰器
     */
    public static void demonstrateDataStream() {
        System.out.println("\n3. 数据流装饰器:");
        
        try {
            // 写入数据
            FileOutputStream fos = new FileOutputStream("data.bin");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            DataOutputStream dos = new DataOutputStream(bos);
            
            dos.writeInt(123);
            dos.writeUTF("装饰器模式");
            dos.writeDouble(3.14);
            
            dos.close();
            System.out.println("数据写入完成");
            
            // 读取数据
            FileInputStream fis = new FileInputStream("data.bin");
            BufferedInputStream bis = new BufferedInputStream(fis);
            DataInputStream dis = new DataInputStream(bis);
            
            System.out.println("读取数据:");
            System.out.println("Int: " + dis.readInt());
            System.out.println("String: " + dis.readUTF());
            System.out.println("Double: " + dis.readDouble());
            
            dis.close();
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 演示压缩流装饰器
     */
    public static void demonstrateCompressionStream() {
        System.out.println("\n4. 压缩流装饰器:");
        
        try {
            // 压缩写入
            FileOutputStream fos = new FileOutputStream("compressed.gz");
            GZIPOutputStream gzos = new GZIPOutputStream(fos);
            BufferedOutputStream bos = new BufferedOutputStream(gzos);
            
            String text = "这是一个被压缩的文本内容,使用GZIP装饰器进行压缩处理。";
            bos.write(text.getBytes());
            
            bos.close();
            System.out.println("压缩文件写入完成");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

装饰器模式的优点

1. 灵活性高

java 复制代码
// 可以动态组合功能
Beverage coffee = new Espresso();
coffee = new MilkDecorator(coffee);    // 随时添加牛奶
coffee = new SugarDecorator(coffee);   // 随时添加糖
// 不需要修改原有类

2. 符合开闭原则

java 复制代码
// 对扩展开放,对修改关闭
// 新增装饰器不需要修改现有代码
public class NewCondimentDecorator extends CondimentDecorator {
    // 新增装饰器,不影响其他类
}

3. 避免类爆炸

java 复制代码
// 不使用装饰器模式需要:
// EspressoWithMilk, EspressoWithSugar, EspressoWithMilkAndSugar...
// 使用装饰器模式只需要:Espresso + 各种装饰器组合

装饰器模式的缺点

1. 增加系统复杂度

java 复制代码
// 多层装饰可能使代码难以理解
TextComponent text = new BorderDecorator(
    new ColorDecorator(
        new UpperCaseDecorator(
            new PlainText("hello")
        ), "red"
    ), '*'
);

2. 调试困难

java 复制代码
// 多层包装使得调试时难以确定问题所在
// 需要逐层检查装饰器

适用场景

  1. 在不影响其他对象的情况下,动态、透明地给单个对象添加职责
  2. 处理那些可以撤销的职责
  3. 当不能采用继承的方式对系统进行扩充时

最佳实践

1. 保持接口一致性

java 复制代码
public abstract class Decorator implements Component {
    // 装饰器要实现相同的接口
    // 这样对客户端来说,装饰器和具体组件没有区别
}

2. 保持装饰器的简单性

java 复制代码
public class SimpleDecorator extends Decorator {
    // 每个装饰器只负责一个单一的功能
    // 避免一个装饰器做太多事情
}

3. 注意装饰顺序

java 复制代码
// 装饰顺序可能影响最终结果
TextComponent text = new PlainText("hello");
text = new UpperCaseDecorator(text);   // 先大写
text = new BorderDecorator(text);      // 后加边框
// 与反向顺序结果可能不同

装饰器模式 vs 继承

特性 继承 装饰器模式
扩展方式 编译时静态扩展 运行时动态扩展
灵活性 低,继承关系在编译时确定 高,可以在运行时组合
类数量 容易产生类爆炸 类数量可控
维护性 修改父类影响所有子类 装饰器之间相互独立

总结

装饰器模式就像给对象"穿衣服",可以一层一层地添加功能,而且可以随意搭配。

核心价值:

  • 动态添加功能,无需修改原有代码
  • 功能组合灵活,避免类爆炸
  • 符合开闭原则,易于扩展

使用场景:

  • 需要动态、透明地给对象添加功能时
  • 需要为对象添加的功能可能经常变化时
  • 不能使用继承或继承会导致类爆炸时

简单记忆:

想要新功能,包装一下就行!

就像给咖啡加调料,想加什么就加什么。

掌握装饰器模式,能够让你的代码像搭积木一样灵活组合各种功能,创造出无限可能!

相关推荐
朝新_3 小时前
【EE初阶】JVM
java·开发语言·网络·jvm·笔记·算法·javaee
如果丶可以坑3 小时前
maven无法获取依赖问题
java·maven·1024程序员节
blammmp4 小时前
RabbitMQ :概述,Web界面介绍,快速上手,工作模式
java·分布式·rabbitmq
何中应4 小时前
如何截取PDF内容为图片
java·开发语言·后端·pdf
哈皮Superman4 小时前
【Research】MagicFuzzer: Scalable deadlock detection for large-scale applications
java·开发语言·数据库
I'm Jie5 小时前
(二)Gradle 依赖仓库及安全凭证配置
java·spring boot·spring·gradle·maven
牢七5 小时前
CATWIFI
java