【设计模式】装饰器模式大白话讲解
装饰器模式大白话讲解
一句话概括
就像给手机加配件:手机本身功能不变,加个壳防摔,加个膜防刮,加个镜头增强拍照




现实生活比喻
场景1:手机配件
- 基础手机:打电话、发短信
- 加手机壳:还是能打电话,但多了防摔功能
- 加钢化膜:还是能触摸,但多了防刮功能
- 加外接镜头:还是能拍照,但效果更好了
场景2:咖啡加料
- 基础咖啡:黑咖啡
- 加牛奶:变成拿铁
- 加糖:变成甜咖啡
- 加奶油:变成奶油咖啡
- 可以任意组合:牛奶+糖,牛奶+奶油,糖+奶油,牛奶+糖+奶油…
完整代码示例
场景:咖啡店点餐系统
/*** 装饰器模式 - 咖啡店示例*/
public class Main {public static void main(String[] args) {System.out.println("=== 咖啡店菜单 ===");// 1. 基础咖啡Coffee blackCoffee = new BlackCoffee();System.out.println(blackCoffee.getDescription() + " 价格: ¥" + blackCoffee.cost());// 2. 加配料的咖啡Coffee milkCoffee = new MilkDecorator(new BlackCoffee());System.out.println(milkCoffee.getDescription() + " 价格: ¥" + milkCoffee.cost());Coffee sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new BlackCoffee()));System.out.println(sugarMilkCoffee.getDescription() + " 价格: ¥" + sugarMilkCoffee.cost());// 3. 豪华组合Coffee premiumCoffee = new WhippedCreamDecorator(new ChocolateDecorator(new SugarDecorator(new MilkDecorator(new Espresso()))));System.out.println(premiumCoffee.getDescription() + " 价格: ¥" + premiumCoffee.cost());// 4. 不同基础咖啡的组合Coffee icedCoffee = new IceDecorator(new SugarDecorator(new Latte()));System.out.println(icedCoffee.getDescription() + " 价格: ¥" + icedCoffee.cost());}
}/*** 抽象组件 - 咖啡接口*/
interface Coffee {String getDescription();  // 获取描述double cost();           // 计算价格
}/*** 具体组件 - 基础咖啡种类*/
class BlackCoffee implements Coffee {@Overridepublic String getDescription() {return "黑咖啡";}@Overridepublic double cost() {return 15.0;}
}class Espresso implements Coffee {@Overridepublic String getDescription() {return "浓缩咖啡";}@Overridepublic double cost() {return 18.0;}
}class Latte implements Coffee {@Overridepublic String getDescription() {return "拿铁咖啡";}@Overridepublic double cost() {return 20.0;}
}/*** 抽象装饰器 - 配料基类*/
abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}@Overridepublic String getDescription() {return decoratedCoffee.getDescription();}@Overridepublic double cost() {return decoratedCoffee.cost();}
}/*** 具体装饰器 - 各种配料*/
class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + " + 牛奶";}@Overridepublic double cost() {return decoratedCoffee.cost() + 3.0;}
}class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + " + 糖";}@Overridepublic double cost() {return decoratedCoffee.cost() + 1.0;}
}class WhippedCreamDecorator extends CoffeeDecorator {public WhippedCreamDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + " + 奶油";}@Overridepublic double cost() {return decoratedCoffee.cost() + 5.0;}
}class ChocolateDecorator extends CoffeeDecorator {public ChocolateDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + " + 巧克力";}@Overridepublic double cost() {return decoratedCoffee.cost() + 4.0;}
}class IceDecorator extends CoffeeDecorator {public IceDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + " + 冰";}@Overridepublic double cost() {return decoratedCoffee.cost() + 2.0;}
}
运行结果
=== 咖啡店菜单 ===
黑咖啡 价格: ¥15.0
黑咖啡 + 牛奶 价格: ¥18.0
黑咖啡 + 牛奶 + 糖 价格: ¥19.0
浓缩咖啡 + 牛奶 + 糖 + 巧克力 + 奶油 价格: ¥31.0
拿铁咖啡 + 糖 + 冰 价格: ¥23.0
更实用的例子:IO流系统
/*** Java IO流就是装饰器模式的经典应用*/
public class IOExample {public static void main(String[] args) {// Java IO流中的装饰器模式// InputStream就是抽象组件// FileInputStream、ByteArrayInputStream就是具体组件// BufferedInputStream、DataInputStream就是装饰器System.out.println("=== Java IO流中的装饰器模式 ===");// 模拟Java IO的使用方式try {// 基础文件流InputStream fileStream = new FileInputStream("data.txt");System.out.println("基础文件流: 只能按字节读取");// 加缓冲装饰器InputStream bufferedStream = new BufferedInputStream(fileStream);System.out.println("缓冲流: 可以批量读取,提高效率");// 加数据装饰器InputStream dataStream = new DataInputStream(bufferedStream);System.out.println("数据流: 可以读取各种数据类型");// 组合使用InputStream advancedStream = new DataInputStream(new BufferedInputStream(new FileInputStream("data.txt")));System.out.println("组合流: 既有缓冲又有数据类型支持");} catch (Exception e) {e.printStackTrace();}}
}// 模拟Java IO的装饰器结构
interface InputStream {int read();void close();
}class FileInputStream implements InputStream {private String filename;public FileInputStream(String filename) {this.filename = filename;}@Overridepublic int read() {System.out.println("从文件 " + filename + " 读取一个字节");return 0;}@Overridepublic void close() {System.out.println("关闭文件流");}
}// 抽象装饰器
abstract class FilterInputStream implements InputStream {protected InputStream in;public FilterInputStream(InputStream in) {this.in = in;}@Overridepublic int read() {return in.read();}@Overridepublic void close() {in.close();}
}// 具体装饰器
class BufferedInputStream extends FilterInputStream {public BufferedInputStream(InputStream in) {super(in);}@Overridepublic int read() {System.out.println("从缓冲区读取数据...");return super.read();}public int read(byte[] buffer) {System.out.println("批量读取 " + buffer.length + " 字节到缓冲区");return buffer.length;}
}class DataInputStream extends FilterInputStream {public DataInputStream(InputStream in) {super(in);}@Overridepublic int read() {return super.read();}public int readInt() {System.out.println("读取int类型数据");return 0;}public double readDouble() {System.out.println("读取double类型数据");return 0.0;}
}
装饰器模式的核心结构
      Component(抽象组件)↑
ConcreteComponent(具体组件) ← Decorator(抽象装饰器)↑ConcreteDecorator(具体装饰器)
关键特征:
- 相同接口:装饰器和被装饰对象实现相同接口
- 组合关系:装饰器包含被装饰对象的引用
- 透明装饰:客户端不知道是否被装饰
- 动态添加:运行时动态添加功能
装饰器 vs 继承
继承方式(不灵活):
class Coffee { ... }
class MilkCoffee extends Coffee { ... }
class SugarCoffee extends Coffee { ... }
class MilkSugarCoffee extends Coffee { ... } // 类爆炸!
装饰器方式(灵活):
Coffee coffee = new MilkDecorator(new SugarDecorator(new BlackCoffee()));
// 任意组合,无需创建新类
适用场景(大白话版)
✅ 适合用装饰器的场景:
- 
动态添加功能 // 运行时决定加什么功能 if (userWantsMilk) {coffee = new MilkDecorator(coffee); } if (userWantsSugar) {coffee = new SugarDecorator(coffee); }
- 
避免类爆炸 // 如果有10种配料,继承需要2^10=1024个类 // 装饰器只需要10个装饰器类,任意组合
- 
不影响原有对象 // 只是包装,不修改原对象 // 可以多次装饰,层层包装
❌ 不适合的场景:
- is-a关系:如果确实是"是一种"的关系,用继承
- 功能变化很大:如果要彻底改变接口,用适配器
- 简单扩展:如果只需要简单扩展,直接修改类可能更简单
优缺点
优点:
- 灵活扩展:可以动态添加功能
- 符合开闭原则:不修改原有代码就能扩展
- 避免类爆炸:组合代替继承
- 职责清晰:每个装饰器只负责一个功能
缺点:
- 复杂度增加:多层装饰理解起来有点绕
- 调试困难:调用链较长,调试不太直观
- 对象识别:被装饰后的对象类型发生变化
与其它模式对比
| 模式 | 目的 | 关键区别 | 
|---|---|---|
| 装饰器模式 | 动态添加功能 | 保持接口,增强功能 | 
| 适配器模式 | 接口转换 | 改变接口,功能不变 | 
| 策略模式 | 算法替换 | 整体替换算法 | 
| 组合模式 | 部分-整体 | 树形结构,统一操作 | 
总结
装饰器模式就是:
- 包装纸:一层层包装,每层增加一点功能
- 俄罗斯套娃:大娃套小娃,功能叠加
- 乐高积木:基础块+装饰块,任意组合
核心口诀:
功能扩展要动态,
继承爆炸很头痛。
装饰模式来包装,
层层叠加真轻松!
就像现实中的:
- ☕ 咖啡加料:基础咖啡+牛奶+糖+奶油…
- 📦 礼品包装:礼物+包装纸+彩带+贺卡
- 🎮 游戏装备:基础角色+武器+防具+饰品
- 🏠 房屋装修:毛坯房+水电+墙面+家具
记住:当你需要动态、透明地给对象添加功能,并且不想使用继承时,考虑装饰器模式!
