|
| 1 | +# 装饰者模式 |
| 2 | + |
| 3 | +> 装饰者模式是在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。 |
| 4 | +> |
| 5 | +> 本章节的示例需要 babel 支持修饰器模式 |
| 6 | +
|
| 7 | +装饰者模式非常贴合 JavaScript 动态语言的特性,因为我们可以轻易的改变某个对象,但同时,因为函数是`一等公民`,所以我们会避免直接改写某个函数,来保护代码的可维护性和可扩展性。 |
| 8 | + |
| 9 | +其实就像我们拍照后添加的`滤镜`,不同的滤镜给照片赋予了不同的意境,这就是装饰者模式,通过滤镜装饰了照片,而并没有修改照片本身就给其添加了功能。 |
| 10 | + |
| 11 | +下面来举一个例子: |
| 12 | + |
| 13 | +#### 初始实例 |
| 14 | + |
| 15 | +加入此时你是以为勇者 但勇者当前只有初始数值 |
| 16 | + |
| 17 | +```javascript |
| 18 | +class Warrior { |
| 19 | + constructor(atk=50, def=50, hp=100, mp=100) { |
| 20 | + this.init(atk,def,hp,mp) |
| 21 | + } |
| 22 | + |
| 23 | + init(atk, def, hp, mp) { |
| 24 | + this.atk = atk |
| 25 | + this.def = def |
| 26 | + this.hp = hp |
| 27 | + this.mp = mp |
| 28 | + } |
| 29 | + |
| 30 | + toString() { |
| 31 | + return `攻击力:${this.atk}, 防御力: ${this.def}, 血量: ${this.hp}, 法力值: ${this.mp}` |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | + |
| 36 | +const Reaper = new Warrior() |
| 37 | + |
| 38 | +console.log(`勇者状态 => ${Reaper}`) // 勇者状态 => 攻击力:50, 防御力: 50, 血量: 100, 法力值: 100 |
| 39 | +``` |
| 40 | + |
| 41 | +#### 装饰器的使用 |
| 42 | + |
| 43 | +之后我们为勇者配备一把圣剑,创建一个 `decorateSword` 方法,注意这个方法是装饰在`init` 上的 |
| 44 | + |
| 45 | +```javascript |
| 46 | +// 本质是使用了 Object.defineProperty 方法 |
| 47 | +function decorateSword(target, key, descriptor) { |
| 48 | + // 首先获取到 init 方法 |
| 49 | + const initMethod = descriptor.value |
| 50 | + // 宝剑添加攻击力 100 点 |
| 51 | + let moreAtk = 100 |
| 52 | + let returnObj |
| 53 | + descriptor.value = (...args) => { |
| 54 | + args[0] += moreAtk |
| 55 | + returnObj = initMethod.apply(target, args) |
| 56 | + return returnObj |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | + |
| 61 | + |
| 62 | +class Warrior { |
| 63 | + constructor(atk=50, def=50, hp=100, mp=100) { |
| 64 | + this.init(atk,def,hp,mp) |
| 65 | + } |
| 66 | + @decorateSword |
| 67 | + init(atk, def, hp, mp) { |
| 68 | + this.atk = atk |
| 69 | + this.def = def |
| 70 | + this.hp = hp |
| 71 | + this.mp = mp |
| 72 | + } |
| 73 | + |
| 74 | + toString() { |
| 75 | + return `攻击力:${this.atk}, 防御力: ${this.def}, 血量: ${this.hp}, 法力值: ${this.mp}` |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | + |
| 80 | +const Reaper = new Warrior() |
| 81 | + |
| 82 | +console.log(`勇者状态 => ${Reaper}`) // 勇者状态 => 攻击力:150, 防御力: 50, 血量: 100, 法力值: 100 |
| 83 | +``` |
| 84 | + |
| 85 | +#### 装饰器的叠加 |
| 86 | + |
| 87 | +现在,我们这位勇者的防御力还太低了,我们需要为勇者再增添一个护甲 |
| 88 | + |
| 89 | +```typescript |
| 90 | + |
| 91 | +// 省略decorateSword |
| 92 | + |
| 93 | +function decorateArmour(target, key, descriptor) { |
| 94 | + // 首先获取到 init 方法 |
| 95 | + const initMethod = descriptor.value |
| 96 | + // 护甲添加防御力 100 点 |
| 97 | + let moreDef = 100 |
| 98 | + let returnObj |
| 99 | + descriptor.value = (...args) => { |
| 100 | + args[1] += moreDef |
| 101 | + returnObj = initMethod.apply(target, args) |
| 102 | + return returnObj |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | + |
| 107 | +class Warrior { |
| 108 | + constructor(atk=50, def=50, hp=100, mp=100) { |
| 109 | + this.init(atk,def,hp,mp) |
| 110 | + } |
| 111 | + @decorateSword |
| 112 | + @decorateArmour |
| 113 | + init(atk, def, hp, mp) { |
| 114 | + this.atk = atk |
| 115 | + this.def = def |
| 116 | + this.hp = hp |
| 117 | + this.mp = mp |
| 118 | + } |
| 119 | + |
| 120 | + toString() { |
| 121 | + return `攻击力:${this.atk}, 防御力: ${this.def}, 血量: ${this.hp}, 法力值: ${this.mp}` |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | + |
| 126 | +const Reaper = new Warrior() |
| 127 | + |
| 128 | +console.log(`勇者状态 => ${Reaper}`) // 勇者状态 => 攻击力:150, 防御力: 150, 血量: 100, 法力值: 100 |
| 129 | +``` |
| 130 | + |
| 131 | +我们成功的让勇者升级了,他终于可以打败大魔王了(没准还是个史莱姆) |
| 132 | + |
| 133 | +### 总结: |
| 134 | + |
| 135 | +装饰者一般也用来实现 AOP (面向切面编程)利用AOP可以对业务逻辑的各个部分进行隔离,也可以隔离业务无关的功能比如日志上报、异常处理等,从而使得业务逻辑各部分之间的耦合度降低,提高业务无关的功能的复用性,也就提高了开发的效率。 |
| 136 | + |
| 137 | +装饰者模式与代理模式类似,但代理模式的意图是不直接访问实体,为实体提供一个替代者,实体内定义了关键功能,而代理提供或拒绝对实体的访问,或者一些其他额外的事情。而装饰者模式的作用就是为对象动态地加入行为。 |
| 138 | + |
0 commit comments