装饰器模式
Decorator — 动态给对象增加功能,而不改变原始类
一句话理解
像"套娃"一样,在不修改原始对象的前提下,一层一层地给它叠加新功能。
解决什么问题
- 想给一个对象增加功能,但又不想改原始类
- 如果用继承来扩展,子类数量会爆炸式增长(加糖咖啡、加奶咖啡、加糖加奶咖啡……)
- 需要动态组合功能,而不是在编译期写死
怎么写
// 抽象组件
public interface Coffee {
String getDescription();
double getCost();
}
// 基础实现
public class SimpleCoffee implements Coffee {
public String getDescription() { return "普通咖啡"; }
public double getCost() { return 10.0; }
}
// 装饰器基类:也实现 Coffee 接口,内部持有一个 Coffee
public abstract class CoffeeDecorator implements Coffee {
protected final Coffee coffee;
public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }
}
// 具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) { super(coffee); }
public String getDescription() { return coffee.getDescription() + " + 牛奶"; }
public double getCost() { return coffee.getCost() + 3.0; }
}
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) { super(coffee); }
public String getDescription() { return coffee.getDescription() + " + 糖"; }
public double getCost() { return coffee.getCost() + 1.0; }
}// 使用:像套娃一样层层包装
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee); // 加牛奶
coffee = new SugarDecorator(coffee); // 再加糖
System.out.println(coffee.getDescription()); // 普通咖啡 + 牛奶 + 糖
System.out.println(coffee.getCost()); // 14.0关键点:装饰器和被装饰对象实现相同的接口,所以可以无限嵌套。
前端中的装饰器
// TypeScript 装饰器(语法糖)
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`调用 ${key},参数:`, args)
return original.apply(this, args)
}
}
class UserService {
@Log
getUser(id: number) { /* ... */ }
}// React 高阶组件(HOC)也是装饰器思想
function withAuth(Component) {
return function AuthWrapper(props) {
if (!isLoggedIn()) return <LoginPage />
return <Component {...props} />
}
}使用场景
- Java I/O:
new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")))就是经典的装饰器嵌套 - Spring 中的各种 Wrapper 类
- Python 的
@decorator语法 - React 的高阶组件(HOC)
- 中间件模式(Express/Koa)