Mixins

状态: 初稿

介绍

除传统 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
// Disposable Mixin
class Disposable {
isDisposed: boolean;
dispose() {
this.isDisposed = true;
}

}

// Activatable Mixin
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);

////////////////////////////////////////
// In your runtime library somewhere
////////////////////////////////////////

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
// Disposable Mixin
class Disposable {
isDisposed: boolean;
dispose() {
this.isDisposed = true;
}

}

// Activatable Mixin
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 类继承 DisposableActivatable, 我们用 SmartObject 接口继承它们. 由于定义合并, SmartObject 接口将混合进 SmartObject 类.

它视类为接口, 只把 Disposable 和 Activatable 背后的类型混合进 SmartObject, 不包括实现. 这也说明我们需要为类提供实现.
但是, 这恰是想用 mixin 避免的.

最后一步, 我们混合所有 mixin 至类实现.

1
applyMixins(SmartObject, [Disposable, Activatable]);

最后, 我们创建一个工具函数帮助我们完成混合.
它会遍历每个 mixin 的属性, 拷贝至目标类, 用它们的实现填充用于占位的属性.

1
2
3
4
5
6
7
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));
});
});
}
如果这篇文章对您有用,可以考虑打赏:)