编程自学指南:java程序设计开发,Java 装饰模式(Decorator)
一、课程信息
学习目标
- 理解装饰模式的核心思想:动态扩展对象功能
- 掌握四大核心角色的设计方法
- 能通过装饰模式替代继承,避免类爆炸
- 区分装饰模式与适配器、代理模式的差异
二、课程导入:生活中的装饰
🌰 场景 1:星巴克咖啡
- 基础咖啡(黑咖啡)→ 加奶 → 加糖浆 → 加奶油
- 传统继承方式:每加一种配料创建一个子类(拿铁 = 咖啡 + 奶,焦糖拿铁 = 拿铁 + 糖浆),导致类爆炸
- 装饰模式方案:用装饰器动态包装基础咖啡
三、核心概念与角色
1. 模式定义
动态地给一个对象添加额外的职责,就像给房子装修一样。
核心:通过组合替代继承,实现功能的灵活扩展
2. 四大核心角色
角色名称 | 作用 | 咖啡案例类比 |
---|---|---|
抽象构件 | 定义对象的核心功能接口 | Beverage (有cost() 方法) |
具体构件 | 核心功能的具体实现 | Coffee (黑咖啡) |
装饰器抽象类 | 继承 / 实现抽象构件,持有被装饰对象的引用 | CondimentDecorator (配料基类) |
具体装饰器 | 实现额外功能,调用被装饰对象的方法 | MilkDecorator 、SugarDecorator |
四、基础实现与案例
🔧 案例 1:星巴克咖啡配料(核心案例)
步骤 1:定义抽象构件
// 饮料抽象类
abstract class Beverage {
protected String description = "未知饮料";
public String getDescription() { return description; }
public abstract double cost();
}
步骤 2:实现具体构件
// 黑咖啡
class Coffee extends Beverage {
public Coffee() { description = "黑咖啡"; }
public double cost() { return 10.0; }
}
// 茶
class Tea extends Beverage {
public Tea() { description = "茶"; }
public double cost() { return 8.0; }
}
步骤 3:定义装饰器抽象类
// 配料装饰器(继承自Beverage)
abstract class CondimentDecorator extends Beverage {
protected Beverage beverage; // 持有被装饰的饮料对象
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
步骤 4:实现具体装饰器
// 加奶
class MilkDecorator extends CondimentDecorator {
public MilkDecorator(Beverage beverage) {
super(beverage);
description = beverage.getDescription() + ", 加奶";
}
public double cost() {
return beverage.cost() + 2.0; // 奶的价格
}
}
// 加糖浆
class SugarDecorator extends CondimentDecorator {
public SugarDecorator(Beverage beverage) {
super(beverage);
description = beverage.getDescription() + ", 加糖浆";
}
public double cost() {
return beverage.cost() + 1.5;
}
}
步骤 5:客户端调用
public class CoffeeShop {
public static void main(String[] args) {
// 基础咖啡加奶和糖浆
Beverage coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription() + ",价格:" + coffee.cost());
// 输出:黑咖啡, 加奶, 加糖浆,价格:13.5
}
}
五、进阶案例:手机功能扩展
需求:为手机动态添加拍照、音乐、快充功能
1. 抽象构件:手机核心功能
interface Phone {
void use(); // 使用手机
}
2. 具体构件:基础手机
class BasicPhone implements Phone {
public void use() {
System.out.println("打电话、发短信");
}
}
3. 装饰器抽象类
abstract class PhoneDecorator implements Phone {
protected Phone phone;
public PhoneDecorator(Phone phone) {
this.phone = phone;
}
}
4. 具体装饰器
// 拍照功能
class CameraDecorator extends PhoneDecorator {
public CameraDecorator(Phone phone) {
super(phone);
}
public void use() {
phone.use(); // 先执行原有功能
takePhoto(); // 新增拍照功能
}
private void takePhoto() {
System.out.println("拍照:咔嚓!");
}
}
// 快充功能
class FastChargeDecorator extends PhoneDecorator {
public FastChargeDecorator(Phone phone) {
super(phone);
}
public void use() {
phone.use();
chargeFast();
}
private void chargeFast() {
System.out.println("快充已开启,30分钟充满");
}
}
六、装饰模式 vs 继承
特性 | 继承 | 装饰模式 |
---|---|---|
扩展方式 | 静态(编译时确定) | 动态(运行时组合) |
类数量 | 多(每个功能组合一个类) | 少(基础类 + 装饰器) |
灵活性 | 低(需修改继承层次) | 高(自由组合装饰器) |
代码复用 | 差(子类只能继承父类功能) | 好(装饰器可重复使用) |
七、JDK 中的装饰模式
1. InputStream
系列
InputStream is = new FileInputStream("data.txt");
is = new BufferedInputStream(is); // 装饰为缓冲流
is = new DataInputStream(is); // 装饰为数据输入流
2. StringBuilder
的 append
方法
通过方法调用链动态添加内容,本质是方法级别的装饰
八、适用场景与优缺点
✅ 优点
- 动态扩展:无需修改原有代码,随时添加 / 删除功能
- 解耦:功能模块独立,可自由组合
- 避免类爆炸:替代多层继承
⚠️ 缺点
- 多层装饰:过度使用会导致代码复杂(如
InputStream
嵌套过深) - 性能损耗:每层装饰增加方法调用开销
🔥 适用场景
- 功能动态叠加:如日志功能、缓存功能
- 替代继承:避免子类数量爆炸
- Java IO/NIO:典型的装饰模式应用
九、课堂练习
练习 1:扩展咖啡案例
需求:新增 "奶油" 配料,价格 3 元,修改描述为 "加奶油"
练习 2:文本处理装饰器
需求:
- 基础功能:输出文本
- 装饰器 1:添加前后缀(如 <<文本>>)
- 装饰器 2:转为大写
十、课程总结
知识图谱:
装饰模式
↳ 核心:组合替代继承,动态扩展功能
↳ 角色:抽象构件→具体构件→装饰器抽象→具体装饰器
↳ 优势:灵活扩展、避免类爆炸
↳ 与继承区别:动态vs静态,组合vs父子
口诀记忆:
“装饰模式像装修,功能动态往上加,
继承层次太复杂,组合装饰来救场,
构件装饰分清楚,层层包装功能强!”
十一、课后作业
必做 1:实现日志装饰器
需求:
- 基础功能:执行数据库操作
- 装饰器 1:记录操作开始 / 结束时间
- 装饰器 2:记录操作参数
必做 2:分析 Java IO 装饰器链
任务:
- 写出
BufferedReader
的装饰器链(从FileReader
开始) - 说明每个装饰器的作用