状态: 初稿
介绍
除传统 OO 层次结构外,另一种流行的从可重用组件构建类的方式是合并部分类(译注: 在本章即 mixin, 或 mix-in).
你或许熟悉 Scala 等语言的 mixins 或特征(traits)的概念, 同样, 这些模式在 JavaScript 社区也获得了一些人气.
Mixin 实例
在下面的代码中, 我们向你展示用 TypeScript 模拟 mixins.
随后, 我们分析它是怎么工作的.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| class Disposable { isDisposed: boolean; dispose() { this.isDisposed = true; }
}
class Activatable { isActive: boolean; activate() { this.isActive = true; } deactivate() { this.isActive = false; } }
class SmartObject { constructor() { setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500); }
interact() { this.activate(); } }
interface SmartObject extends Disposable, Activatable {} applyMixins(SmartObject, [Disposable, Activatable]);
let smartObj = new SmartObject(); setTimeout(() => smartObj.interact(), 1000);
function applyMixins(derivedCtor: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name)); }); }); }
|
理解该实例
这个例子以两个类定义开始, 它们将作为我们的 mixin.
可以看到每个类都聚焦在特定的活动或能力上.
我们稍后会混合它们, 综合两者能力形成一个新类.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Disposable { isDisposed: boolean; dispose() { this.isDisposed = true; }
}
class Activatable { isActive: boolean; activate() { this.isActive = true; } deactivate() { this.isActive = false; } }
|
下一步, 我们再创建一个类, 管理这两个 mixin 的组合.
我们详细观察这部分, 看它是怎么做的:
1 2 3 4 5
| class SmartObject { ... }
interface SmartObject extends Disposable, Activatable {}
|
首先你会注意到, 比起试图在 SmartObject
类继承 Disposable
和 Activatable
, 我们用 SmartObject
接口继承它们. 由于定义合并, SmartObject
接口将混合进 SmartObject
类.
它视类为接口, 只把 Disposable 和 Activatable 背后的类型混合进 SmartObject, 不包括实现. 这也说明我们需要为类提供实现.
但是, 这恰是想用 mixin 避免的.
最后一步, 我们混合所有 mixin 至类实现.
1
| applyMixins(SmartObject, [Disposable, Activatable]);
|
最后, 我们创建一个工具函数帮助我们完成混合.
它会遍历每个 mixin 的属性, 拷贝至目标类, 用它们的实现填充用于占位的属性.
1 2 3 4 5 6 7 8
| function applyMixins(derivedCtor: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name)); }); }); }
|