什么是装饰器模式?
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给对象添加额外功能。与继承不同,装饰器模式更加灵活,可以在不修改原有类的情况下扩展功能。
生活比喻
想象你买手机。基础款手机只有打电话功能。但你可以加钱"装饰"它:贴膜(防刮)、加手机壳(防摔)、装内存卡(扩展存储)。这些都是"装饰器",可以自由组合,按需加装。装饰器模式就是这样——在对象外面包一层,动态添加功能。
装饰器模式结构:
Component(组件接口)
│
┌────────┴────────┐
▼ ▼
ConcreteComponent Decorator(装饰器)
(基础对象) │
│ 持有Component引用
▼
┌───────────────┐
│ConcreteDecorator│
│ (具体装饰器) │
│ + 添加的功能 │
└───────────────┘
咖啡例子:
Coffee (接口)
│
┌────────┴────────┐
▼ ▼
SimpleCoffee CoffeeDecorator
(原味咖啡) │
▼
┌───────────────┐
│ MilkDecorator │
│ +牛奶 │
└───────────────┘
为什么需要装饰器模式?
继承的局限性
// 如果用继承扩展咖啡类...
class SimpleCoffee { double getCost() { return 2.0; } }
class MilkCoffee { double getCost() { return 2.5; } }
class SugarCoffee { double getCost() { return 2.2; } }
class MilkSugarCoffee { double getCost() { return 2.7; } }
// 牛奶+糖+摩卡+... 类的数量爆炸!
如果用装饰器模式,只需要定义基础组件和装饰器,自由组合:
// 咖啡 + 牛奶 + 糖 + 摩卡 = MilkSugarMochaCoffee
Coffee coffee = new MochaDecorator(new SugarDecorator(new MilkDecorator(new SimpleCoffee())));
完整实现
1. 定义组件接口
// 组件接口 - 定义组件和装饰器的公共接口
public interface Coffee {
String getDescription();
double getCost();
}
2. 实现具体组件
// 基础组件 - 最简单的咖啡
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "原味咖啡";
}
@Override
public double getCost() {
return 2.0;
}
}
3. 创建装饰器基类
// 装饰器基类 - 持有组件引用,实现组件接口
public abstract class CoffeeDecorator implements Coffee {
// 持有被装饰的对象
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double getCost() {
return coffee.getCost();
}
}
4. 实现具体装饰器
// 牛奶装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 牛奶";
}
@Override
public double getCost() {
return coffee.getCost() + 0.5;
}
}
// 糖装饰器
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 糖";
}
@Override
public double getCost() {
return coffee.getCost() + 0.2;
}
}
// 摩卡装饰器
public class MochaDecorator extends CoffeeDecorator {
public MochaDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 摩卡";
}
@Override
public double getCost() {
return coffee.getCost() + 1.0;
}
}
// 奶油装饰器
public class WhipDecorator extends CoffeeDecorator {
public WhipDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 奶油";
}
@Override
public double getCost() {
return coffee.getCost() + 0.8;
}
}
5. 客户端使用
public class DecoratorDemo {
public static void main(String[] args) {
System.out.println("===== 点一杯原味咖啡 =====");
Coffee coffee1 = new SimpleCoffee();
printCoffee(coffee1);
System.out.println("\n===== 点一杯拿铁(咖啡+牛奶)=====");
Coffee coffee2 = new MilkDecorator(new SimpleCoffee());
printCoffee(coffee2);
System.out.println("\n===== 点一杯特调(咖啡+牛奶+糖+摩卡)=====");
Coffee coffee3 = new MochaDecorator(
new SugarDecorator(
new MilkDecorator(
new SimpleCoffee())));
printCoffee(coffee3);
System.out.println("\n===== 摩卡星冰乐(摩卡+奶油+糖)=====");
Coffee coffee4 = new WhipDecorator(
new MochaDecorator(
new SugarDecorator(
new SimpleCoffee())));
printCoffee(coffee4);
}
static void printCoffee(Coffee coffee) {
System.out.println("描述: " + coffee.getDescription());
System.out.println("价格: $" + coffee.getCost());
}
}
Java IO中的装饰器模式
Java的I/O流大量使用了装饰器模式:
Java IO装饰器模式:
┌─────────────────────────────────────────────────────────┐
│ 组件接口: InputStream / OutputStream │
└─────────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ FileInput │ │ ByteArray │ │ ...其他 │
│ Stream │ │ InputStream│ │ │
└─────┬─────┘ └───────────┘ └───────────┘
│ │
└─────────┬─────────┘
│ 被装饰对象
┌───────▼───────┐
│ FilterInput │ ← 装饰器基类
│ Stream │
└───────┬───────┘
│ 装饰器
┌───────────┼───────────┬───────────┐
▼ ▼ ▼ ▼
┌───────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐
│Buffered│ │Data │ │GZIP │ │ ...其他 │
│Input │ │InputStream│ │InputStream│ │装饰器 │
│缓冲 │ │数据流 │ │压缩流 │ │ │
└───────┘ └──────────┘ └─────────┘ └──────────┘
import java.io.*;
// 装饰器模式在IO中的应用
public class IODecoratorDemo {
public static void main(String[] args) throws Exception {
// 基础:文件输入流
InputStream fis = new FileInputStream("test.txt");
// 装饰1:缓冲流(加速读取)
BufferedInputStream bis = new BufferedInputStream(fis);
// 装饰2:数据输入流(方便读取数据类型)
DataInputStream dis = new DataInputStream(bis);
// 装饰3:压缩流(自动解压)
GZIPInputStream gis = new GZIPInputStream(dis);
// 现在可以从压缩文件中方便地读取各种类型的数据
// String name = dis.readUTF();
// int age = dis.readInt();
}
}
装饰器模式的优势
开闭原则
扩展功能不需要修改原有代码
自由组合
可以任意组合装饰器,按需添加
单一职责
每个装饰器只负责一种扩展功能
运行时生效
可以在运行时动态添加/移除装饰
装饰器 vs 继承
| 对比项 | 继承 | 装饰器 |
|---|---|---|
| 编译时 | 编译时确定 | 运行时动态组合 |
| 类数量 | 组合爆炸 | 类数量稳定 |
| 灵活性 | 低 | 高 |
| 扩展 | 需要修改代码 | 新增装饰器即可 |
小结
- 装饰器模式动态给对象添加功能,比继承更灵活
- 装饰器持有组件引用,调用被装饰对象的方法并增强
- 可以叠加多个装饰器,自由组合
- 符合开闭原则,不修改原有代码
- Java IO流大量使用装饰器模式