混合

在使用传统的面向对象语言时,一个基于类构建可重用组件的办法是,让一个类基于多个简单的类。你可能对Scala语言中“混合”的概念有所知晓。类似的概念如今在JavaScript社区中也越来越流行。

关于混合的例子

下面的例子展示了TypeScript中的混合:

// 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 implements Disposable, Activatable {
    constructor() {
        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
    }

    interact() {
        this.activate();
    }

    // Disposable
    isDisposed: boolean = false;
    dispose: () => void;
    // Activatable
    isActive: boolean = false;
    activate: () => void;
    deactivate: () => void;
}
applyMixins(SmartObject, [Disposable, Activatable])

var 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 => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        })
    });
}

理解例子

上文的例子以两个类开始,这两个类专注于各自的活动和功能。在后面我们将它们混合入一个新类来同时获取它们两个的功能。

// 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 implements Disposable, Activatable {

你可能很快注意到,我们并没有使用extends关键字,而是使用了implements。这会将类视为接口,然后只使用DisposableActivatable这两个类背后的类型声明,而不是具体实现。这意味着在后面,我们需要自己在类中提供实现细节。这可能并不是我们喜欢做的。

为了不重新提供实现细节,我们创建了同名属性,并且提供了同样的类型。这会告诉编译器这些成员在运行时将会可用,这将让我们不用机械地重新抄写一遍同样的代码:

// Disposable
isDisposed: boolean = false;
dispose: () => void;
// Activatable
isActive: boolean = false;
activate: () => void;
deactivate: () => void;

最后,我们将其混合入类中,补全了具体的实现:

applyMixins(SmartObject, [Disposable, Activatable])

applyMixins是我们创建的一个辅助函数。这将会遍历我们被混合的类的原型,将其的具体实现赋值给最终类:

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        })
    });
}