9. 装饰器模式(Decorator)
9. 装饰器模式(Decorator)
问题:想给对象动态添加功能,而不修改原有类或使用继承。
核心:用包装类包裹原对象,调用时先执行增强逻辑再委托给原对象。
interface Coffee {
cost(): number;
desc(): string;
}
class PlainCoffee implements Coffee {
cost(): number { return 10; }
desc(): string { return '咖啡'; }
}
class MilkDecorator implements Coffee {
#coffee: Coffee;
constructor(coffee: Coffee) { this.#coffee = coffee; }
cost(): number { return this.#coffee.cost() + 3; }
desc(): string { return `${this.#coffee.desc()} + 牛奶`; }
}
class SugarDecorator implements Coffee {
#coffee: Coffee;
constructor(coffee: Coffee) { this.#coffee = coffee; }
cost(): number { return this.#coffee.cost() + 1; }
desc(): string { return `${this.#coffee.desc()} + 糖`; }
}
let myCoffee: Coffee = new PlainCoffee();
myCoffee = new MilkDecorator(myCoffee);
myCoffee = new SugarDecorator(myCoffee);
console.log(myCoffee.desc()); // 咖啡 + 牛奶 + 糖
console.log(myCoffee.cost()); // 14package decorator
import "fmt"
type Coffee interface {
Cost() int
Desc() string
}
type BaseCoffee struct{}
func (b BaseCoffee) Cost() int { return 10 }
func (b BaseCoffee) Desc() string { return "咖啡" }
type MilkDecorator struct{ Coffee Coffee }
func (d MilkDecorator) Cost() int { return d.Coffee.Cost() + 3 }
func (d MilkDecorator) Desc() string { return d.Coffee.Desc() + " + 牛奶" }
type SugarDecorator struct{ Coffee Coffee }
func (d SugarDecorator) Cost() int { return d.Coffee.Cost() + 1 }
func (d SugarDecorator) Desc() string { return d.Coffee.Desc() + " + 糖" }
// 使用
// coffee := BaseCoffee{}
// coffee = MilkDecorator{Coffee: coffee}
// coffee = SugarDecorator{Coffee: coffee}
// fmt.Println(coffee.Desc()) // 咖啡 + 牛奶 + 糖
// fmt.Println(coffee.Cost()) // 14实战场景
- HTTP 中间件增强:给请求处理函数叠加日志、鉴权、限流、缓存、压缩、错误处理等能力。
- 函数性能计时:包装任意函数,在执行前后记录耗时,不侵入原始业务逻辑。
- 权限控制:在 service 方法外包一层权限检查,只有通过后才调用原方法。
- 缓存装饰:对查询函数加缓存,命中时直接返回,未命中时再调用原函数并写入缓存。
- 重试和熔断:远程调用外层包上重试、超时、降级、熔断逻辑,调用者仍然看到同一个接口。
- UI 组件增强:在基础输入框外包错误提示、标签、帮助文本、loading 状态或埋点逻辑。
- 数据流处理:读取文件或网络流时,一层负责解压,一层负责解密,一层负责缓冲,最终仍是同一个读取接口。
- 埋点增强:给按钮点击、表单提交、路由跳转包一层事件上报,不修改原交互逻辑。
- 测试探针:测试时给真实对象包一层 spy,记录调用次数和参数,同时继续调用真实实现。
- 组合式能力开关:同一个核心对象根据配置动态叠加不同能力,比继承出大量子类更灵活。
JS 的高阶函数就是这个思路:
fn = log(timing(fn))一层层包装。