文章目录
-
- 什么是装饰器模式?
- 核心思想
- 生活中的装饰器模式
- 模式结构
- 基础示例:咖啡店系统
-
- [1. 组件接口](#1. 组件接口)
- [2. 具体组件](#2. 具体组件)
- [3. 装饰器抽象类](#3. 装饰器抽象类)
- [4. 具体装饰器](#4. 具体装饰器)
- [5. 客户端使用](#5. 客户端使用)
- 完整示例:文本处理系统
-
- [1. 文本组件接口](#1. 文本组件接口)
- [2. 具体文本组件](#2. 具体文本组件)
- [3. 文本装饰器抽象类](#3. 文本装饰器抽象类)
- [4. 具体文本装饰器](#4. 具体文本装饰器)
- [5. 文本处理器客户端](#5. 文本处理器客户端)
- 实际应用示例:IO流系统
- 装饰器模式的优点
-
- [1. 灵活性高](#1. 灵活性高)
- [2. 符合开闭原则](#2. 符合开闭原则)
- [3. 避免类爆炸](#3. 避免类爆炸)
- 装饰器模式的缺点
-
- [1. 增加系统复杂度](#1. 增加系统复杂度)
- [2. 调试困难](#2. 调试困难)
- 适用场景
- 最佳实践
-
- [1. 保持接口一致性](#1. 保持接口一致性)
- [2. 保持装饰器的简单性](#2. 保持装饰器的简单性)
- [3. 注意装饰顺序](#3. 注意装饰顺序)
- [装饰器模式 vs 继承](#装饰器模式 vs 继承)
- 总结
什么是装饰器模式?
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
核心思想
装饰器模式的核心思想是:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
生活中的装饰器模式
想象一下我们去咖啡店点咖啡:
- 首先点一杯基础咖啡(被装饰对象)
- 然后可以添加牛奶(装饰器)
- 再加糖(另一个装饰器)
- 再加奶油(又一个装饰器)
每个装饰器都在基础咖啡上添加新的特性,但咖啡的本质没有改变。
模式结构
装饰器模式包含四个核心角色:
- 组件接口(Component):定义对象的接口,可以动态地给这些对象添加职责
- 具体组件(ConcreteComponent):定义具体的对象,可以给这个对象添加一些职责
- 装饰器抽象类(Decorator):继承组件接口,并包含一个组件对象的引用
- 具体装饰器(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. 保持接口一致性
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 继承
| 特性 | 继承 | 装饰器模式 |
|---|---|---|
| 扩展方式 | 编译时静态扩展 | 运行时动态扩展 |
| 灵活性 | 低,继承关系在编译时确定 | 高,可以在运行时组合 |
| 类数量 | 容易产生类爆炸 | 类数量可控 |
| 维护性 | 修改父类影响所有子类 | 装饰器之间相互独立 |
总结
装饰器模式就像给对象"穿衣服",可以一层一层地添加功能,而且可以随意搭配。
核心价值:
- 动态添加功能,无需修改原有代码
- 功能组合灵活,避免类爆炸
- 符合开闭原则,易于扩展
使用场景:
- 需要动态、透明地给对象添加功能时
- 需要为对象添加的功能可能经常变化时
- 不能使用继承或继承会导致类爆炸时
简单记忆:
想要新功能,包装一下就行!
就像给咖啡加调料,想加什么就加什么。
掌握装饰器模式,能够让你的代码像搭积木一样灵活组合各种功能,创造出无限可能!