设计模式--装饰器模式:动态扩展对象功能的优雅设计
装饰器模式:动态扩展对象功能的优雅设计
今天我们来深入探讨装饰器模式(Decorator Pattern),一种结构型设计模式,用于在不修改原有类的情况下动态扩展对象功能。装饰器模式通过将对象包装在装饰器类中,灵活添加新行为,遵循开闭原则。本文将带你实现一个简单的装饰器模式示例,适合初学者快速上手,同时为有经验的开发者提供进阶建议和优化思路。
装饰器模式在现实生活中类似手机壳,为核心对象增加额外功能。本文使用 Java 语言,通过一个咖啡订购系统展示装饰器模式的实现。让我们开始吧!
前置准备
在开始之前,确保开发环境已就绪:
- JDK:推荐 JDK 17(也可使用 JDK 8+)。
- IDE:IntelliJ IDEA、Eclipse 或 VS Code,推荐支持 Java 的 IDE。
- 构建工具:Maven(可选,用于管理依赖)。
- 项目结构:创建一个简单的 Java 项目,目录如下:
decorator-pattern-demo ├── src │ ├── main │ │ ├── java │ │ │ └── com.example.decorator │ │ │ ├── beverage │ │ │ ├── decorator │ │ │ └── Main.java │ └── test └── pom.xml
安装环境:
- 确保 JDK 已安装:
java -version
. - Maven(可选):
mvn -version
. - 无需额外依赖,本示例使用纯 Java。
步骤 1: 定义抽象组件
装饰器模式需要一个抽象组件,定义核心功能。在 com.example.decorator.beverage.Beverage
中:
package com.example.decorator.beverage;public interface Beverage {String getDescription();double cost();
}
说明:
Beverage
是目标接口,定义咖啡的描述和价格方法。
步骤 2: 创建具体组件
实现具体的咖啡类。在 com.example.decorator.beverage.Espresso
中:
package com.example.decorator.beverage;public class Espresso implements Beverage {@Overridepublic String getDescription() {return "Espresso";}@Overridepublic double cost() {return 1.99;}
}
说明:
Espresso
是具体组件,提供基础咖啡功能。
步骤 3: 创建抽象装饰器
定义抽象装饰器类,包装组件并实现相同接口。在 com.example.decorator.decorator.CondimentDecorator
中:
package com.example.decorator.decorator;import com.example.decorator.beverage.Beverage;public abstract class CondimentDecorator implements Beverage {protected Beverage beverage;public CondimentDecorator(Beverage beverage) {this.beverage = beverage;}@Overridepublic abstract String getDescription();
}
说明:
CondimentDecorator
持有Beverage
引用,允许嵌套装饰。
步骤 4: 创建具体装饰器
实现具体的装饰器类(如加奶、加糖)。在 com.example.decorator.decorator.Milk
中:
package com.example.decorator.decorator;import com.example.decorator.beverage.Beverage;public class Milk extends CondimentDecorator {public Milk(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", Milk";}@Overridepublic double cost() {return beverage.cost() + 0.50;}
}
在 com.example.decorator.decorator.Sugar
中:
package com.example.decorator.decorator;import com.example.decorator.beverage.Beverage;public class Sugar extends CondimentDecorator {public Sugar(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", Sugar";}@Overridepublic double cost() {return beverage.cost() + 0.20;}
}
说明:
- 每个装饰器扩展功能(如添加“奶”或“糖”),并调整描述和价格。
步骤 5: 客户端代码
在 com.example.decorator.Main
中测试装饰器模式:
package com.example.decorator;import com.example.decorator.beverage.Beverage;
import com.example.decorator.beverage.Espresso;
import com.example.decorator.decorator.Milk;
import com.example.decorator.decorator.Sugar;public class Main {public static void main(String[] args) {// 基础咖啡Beverage beverage = new Espresso();System.out.println(beverage.getDescription() + " $" + beverage.cost());// 加奶beverage = new Milk(beverage);System.out.println(beverage.getDescription() + " $" + beverage.cost());// 再加糖beverage = new Sugar(beverage);System.out.println(beverage.getDescription() + " $" + beverage.cost());}
}
运行输出:
Espresso $1.99
Espresso, Milk $2.49
Espresso, Milk, Sugar $2.69
步骤 6: 运行和测试
-
编译和运行:
- 在 IDE 中运行
Main
类。 - 或使用命令行:
javac src/main/java/com/example/decorator/*.java src/main/java/com/example/decorator/*/*.java java com.example.decorator.Main
- 在 IDE 中运行
-
测试用例:
- 验证基础咖啡的描述和价格。
- 验证添加奶和糖后,描述和价格正确累加。
- 测试多层装饰(如加两次奶)。
-
调试技巧:
- 添加日志:使用
System.out
或 SLF4J 记录装饰过程。 - 检查对象嵌套:在调试器中查看
beverage
的引用链。 - 异常处理:确保装饰器不会接收 null 对象。
- 添加日志:使用
进阶与最佳实践
-
多层装饰:
- 支持动态添加多个装饰器:
Beverage beverage = new Sugar(new Milk(new Espresso()));
- 支持动态添加多个装饰器:
-
异常处理:
- 添加输入验证:
public CondimentDecorator(Beverage beverage) {if (beverage == null) {throw new IllegalArgumentException("Beverage cannot be null");}this.beverage = beverage; }
- 添加输入验证:
-
性能优化:
- 缓存描述字符串,避免重复计算:
private String cachedDescription; @Override public String getDescription() {if (cachedDescription == null) {cachedDescription = beverage.getDescription() + ", Milk";}return cachedDescription; }
- 缓存描述字符串,避免重复计算:
-
测试:
- 使用 JUnit 编写单元测试:
import org.junit.Test; import static org.junit.Assert.*;public class DecoratorTest {@Testpublic void testEspressoWithMilkAndSugar() {Beverage beverage = new Sugar(new Milk(new Espresso()));assertEquals("Espresso, Milk, Sugar", beverage.getDescription());assertEquals(2.69, beverage.cost(), 0.01);} }
- 使用 JUnit 编写单元测试:
-
其他应用场景:
- I/O 流(如 Java 的
InputStream
和BufferedInputStream
)。 - UI 组件装饰(如添加边框或滚动条)。
- I/O 流(如 Java 的
-
资源推荐:书籍《设计模式:可复用面向对象软件的基础》、Refactoring Guru 网站。多实践其他设计模式(如代理模式、工厂模式)。
总结
通过这个装饰器模式示例,你学会了如何动态扩展对象功能,实现了咖啡订购系统的灵活配置。装饰器模式在需要动态添加行为时非常实用,广泛应用于框架设计和功能扩展。