OOP - 我如何模擬一個抽象類別並呼叫它的建構子?
我正在嘗試透過在 TypeScript 中編寫代碼來跟隨一本 C# 設計模式的書。也許這是我的第一次錯誤,但這是我喜歡學習一門語言的方式。
TypeScript 不支持類別的 abstract 關鍵字,所以我正在嘗試模擬它。也許這是我的第二個錯誤。
這是我的介面和類別:
interface IEngine {
getSize(): number;
getTurbo(): boolean;
}
class AbstractEngine implements IEngine {
constructor(private size: number, private turbo: boolean) {
throw "Abstract class";
}
public getSize(): number {
return this.size;
}
public getTurbo(): boolean {
return this.turbo;
}
public toString(): string {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec(this.constructor.toString());
var className = (results && results.length > 1) ? results[1] : '';
return className + " (" + this.size + ")";
}
}
class StandardEngine extends AbstractEngine {
constructor(size: number) {
// not turbo charged
super(size, false);
}
}
當嘗試使用 new AbstractEngine(1, true) 實例化 AbstractEngine 時,我如預期地得到了「抽象類」錯誤。
當嘗試使用 new StandardEngine(9000) 實例化 StandardEngine 時,我也收到“抽象類別”錯誤。
有辦法在 TypeScript 中模擬抽象類別,使其無法被實例化,但仍然能在擴展它的類別中調用 super 嗎?那模擬抽象方法呢,我能保護這些方法並仍然調用 super 方法嗎?
答案#1
截至今天,TypeScript 1.6 已經上線,並且支援 abstract 關鍵字。
abstract class A {
foo(): number { return this.bar(); }
abstract bar(): number;
}
var a = new A(); // error, Cannot create an instance of the abstract class 'A'
class B extends A {
bar() { return 1; }
}
var b = new b(); // success, all abstracts are defined
答案#2
我建議你不要那麼做。當 TypeScript 編譯器實現抽象函數的機制時,才是使用它的時候。但在運行時有效的黑客方法是不可理解的,並且會降低性能。
介面是 TypeScript 的最大優勢。它們應該被大量使用。
你的範例應該這樣寫:
interface Engine {
getSize(): number;
getTurbo(): boolean;
}
class StandardEngine implements Engine {
constructor(private size: number, private turbo: boolean) {
}
public getSize(): number {
return this.size;
}
public getTurbo(): boolean {
return this.turbo;
}
}
最簡單的解決方案通常是最好的。
如果你想在沒有父類別的情況下重複使用程式碼,而這樣父類別就不一定能被使用,《手冊》建議使用混入(Mixins)。混入是一種從幾個不同實體中獲取技能的方法。
或者使用模組,可以保持私有實作(因此可以按你想的方式組織它)並且只匯出介面和工廠。舉個例子:
module MyEngineModule {
export interface Engine {
getSize(): number;
getTurbo(): boolean;
}
export interface StandardEngine extends Engine {
}
export function makeStandardEngine(size: number, turbo: boolean): StandardEngine {
return new ImplStandardEngine(size, turbo);
}
// here classes are private and can inherit or use mixins…
class ImplEngine {
constructor(private size: number, private turbo: boolean) {
}
public getSize(): number {
return this.size;
}
public getTurbo(): boolean {
return this.turbo;
}
}
class ImplStandardEngine extends ImplEngine implements StandardEngine {
}
}
console.log(MyEngineModule.makeStandardEngine(123, true).getSize());
