装饰器模式 ⭐
定义
装饰器模式(Decorator Pattern),在不修改原对象的情况下,动态地给对象增加功能。
符合的设计原则:
- 开放关闭(OCP):扩展功能,不改原代码
- 单一职责(SRP):每个装饰器只做一件事
- 合成复用:用组合代替继承
实现
1. 基于高阶函数
js
// 1. 原本稳如老狗的老业务代码(不要去改它!)
function submitForm() {
console.log("执行:提交表单核心逻辑...");
}
// 2. 写一个装饰器函数:在执行原函数之前,先执行一段新逻辑
function beforeDecorator(originalFn, beforeFn) {
return function(...args) {
beforeFn.apply(this, args); // 先执行增加的功能
return originalFn.apply(this, args); // 再执行原函数
}
}
// 3. 增强功能:比如提交前需要做表单校验
function validate() {
console.log("增强功能:执行复杂的表单校验...");
}
// 4. 把它们组装起来(给裸机套上手机壳)
const enhancedSubmit = beforeDecorator(submitForm, validate);
// 调用增强后的方法
enhancedSubmit();
// 打印输出:
// 增强功能:执行复杂的表单校验...
// 执行:提交表单核心逻辑...
2. 基于 @ 语法糖
js
function readonly(target, name, descriptor) {
descriptor.writable = false
return descriptor
}
class Person {
first = ''
last = ''
constructor() {
this.first = 'A'
this.last = 'B'
}
@readonly
name() {
return `${this.first} ${this.last}`
}
}
let p = new Person()
console.log(p.name())
// p.name = function () {
// console.log(100)
// } // Cannot assign to read only property 'name' of object '#<Person>'
js
function log(target, name, descriptor) {
console.log(target)
console.log(name)
console.log(descriptor)
let oldValue = descriptor.value
descriptor.value = function () {
console.log(`log: calling ${name} width`, arguments)
return oldValue.apply(this, arguments)
}
return descriptor
}
class Math {
@log
add(a, b) {
return a + b
}
}
let math = new Math()
const result = math.add(4, 6)
console.log(result)
应用场景
- React HOC
- Vue composition
- AOP 面向切面编程,业务和系统基础功能分离(日志、统计、鉴权等)
- 改变函数的参数
- 表单验证
vs 继承
继承是“改结构”,装饰器是“加能力”
| 对比 | 继承 | 装饰器 |
|---|---|---|
| 扩展方式 | 修改类结构 | 包装对象 |
| 灵活性 | 低 | 高 |
| 耦合 | 高 | 低 |
| 是否运行时扩展 | ❌ | ✅ |
参考
- ES7 装饰器 - 阮一峰ES6文档