1、装饰模式
设计模式大家都有了解,网上有很多系列教程。
这里只分享 装饰者模式 以及如何使用 ES7 的 decorator
概念。
1.1、装饰模式 v.s. 适配器模式
装饰模式和适配器模式都是 包装模式 (Wrapper Pattern),它们都是通过封装其他对象达到设计的目的的,但是它们的形态有很大区别。
- 适配器模式我们使用的场景比较多,比如连接不同数据库的情况,你需要包装现有的模块接口,从而使之适配数据库 —— 好比你手机使用转接口来适配插座那样;
- 装饰模式不一样,仅仅包装现有的模块,使之 “更加华丽” ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能;
更多区别参见:设计模式——装饰模式(Decorator)
1.2、装饰模式场景 —— 面向 AOP 编程
装饰模式经典的应用是 AOP 编程,比如“日志系统”,日志系统的作用是记录系统的行为操作,它在不影响原有系统的功能的基础上增加记录环节 —— 好比你佩戴了一个智能手环,并不影响你日常的作息起居,但你现在却有了自己每天的行为记录。
更加抽象的理解,可以理解为给数据流做一层filter
,因此 AOP 的典型应用包括 安全检查、缓存、调试、持久化等等。可参考Spring aop 原理及各种应用场景 。
2、使用 ES7 的 decorator
ES7 中增加了一个 decorator
属性,它借鉴自 Python,请参考文章Decorators in ES7。
下面我们以 钢铁侠 为例讲解如何使用 ES7 的 decorator。
以钢铁侠为例,钢铁侠本质是一个人,只是“装饰”了很多武器方才变得那么 NB,不过再怎么装饰他还是一个人。
我们的示例场景是这样的
- 首先创建一个普通的
Man
类,它的抵御值 2,攻击力为 3,血量为 3; - 然后我们让其带上钢铁侠的盔甲,这样他的抵御力增加 100,变成 102;
- 让其带上光束手套,攻击力增加 50,变成 53;
- 最后让他增加“飞行”能力
2.1、【Demo 1】对方法的装饰:装备盔甲
创建 Man 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻击力 this.hp = hp; // 血量 } toString(){ return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`; } } var tony = new Man(); console.log(`当前状态 ===> ${tony}`); // 输出:当前状态 ===> 防御力:2,攻击力:3,血量:3 |
代码直接放在 http://babeljs.io/repl/ 中运行查看结果,记得勾选Experimental
选项和Evaluate
选项
创建 decorateArmour 方法,为钢铁侠装配盔甲——注意 decorateArmour
是装饰在方法init
上的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
function decorateArmour(target, key, descriptor) { const method = descriptor.value; let moreDef = 100; let ret; descriptor.value = (...args)=>{ args[0] += moreDef; ret = method.apply(target, args); return ret; } return descriptor; } class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻击力 this.hp = hp; // 血量 } toString(){ return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`; } } var tony = new Man(); console.log(`当前状态 ===> ${tony}`); // 输出:当前状态 ===> 防御力:102,攻击力:3,血量:3 |
我们先看输出结果,防御力的确增加了 100,看来盔甲起作用了。
初学者这里会有两个疑问:
-
decorateArmour
方法的参数为啥是这三个?可以更换么?
-
decorateArmour
方法为什么返回的是descriptor
这里给出个人的解答作为参考:
- Decorators 的本质是利用了 ES5 的 Object.defineProperty 属性,这三个参数其实是和 Object.defineProperty 参数一致的,因此不能更改,详细分析请见 细说 ES7 JavaScript Decorators
- 可以看看 bable 转换后 的代码,其中有一句是
descriptor = decorator(target, key, descriptor) || descriptor;
,点到为止,这里不详细展开了,可自行看看这行代码的上下文(参考文献中也涉及到这句代码的解释)。
2.2、【Demo 2】装饰器叠加:增加光束手套
在上面的示例中,我们成功为 普通人 增加 “盔甲” 这个装饰;现在我想再给他增加 “光束手套”,希望额外增加 50 点防御值。
Step 1:拷贝一份decorateArmour
方法,改名为decorateLight
,同时修改防御值的属性: