2014-07-18 12 views
6

Sto cercando di seguire un libro di disegni di design C# scrivendo il mio codice in TypeScript. Forse questo è il mio primo errore, ma è un modo che mi piace imparare una lingua.Come si simula una classe astratta e si chiama il suo costruttore?

dattiloscritto non supporta la parola chiave abstract per le classi, quindi sto cercando di simulare. Forse questo è il mio secondo errore.

Ecco la mia interfaccia e classi:

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); 
    } 
} 

Quando si tenta di creare un'istanza di un AbstractEngine con new AbstractEngine(1, true) ottengo un errore "Estratto di classe" come previsto.

Quando si tenta di creare un'istanza di uno StandardEngine con new StandardEngine(9000) Inoltre ottengo un errore "classe astratta".

C'è un modo per simulare una classe astratta a macchina, l'hanno in grado di essere istanziati, ma ancora chiamare eccellente in una classe che estende? E che dire della simulazione di metodi astratti, posso proteggerli e chiamare ancora il metodo super?

risposta

1

io consiglio di non farlo. Quando il compilatore TypeScript implementerà un meccanismo per la funzione astratta, è ora di usarlo. Ma gli hack che funzionano in runtime sono incomprensibili e degradano le prestazioni.

Le interfacce sono la grande forza di TypeScript. Dovrebbero essere usati in maniera massiccia.

Il vostro esempio dovrebbe essere scritto in questo modo:

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; 
    } 
} 

La soluzione più semplice è spesso la migliore.

Se si vuole riutilizzare il codice senza una classe genitore che avrebbe poi necessariamente essere usate, il Handbook suggests Mixins. Le mixine sono un modo per far fronte alle abilità di diverse entità distinte.

Oppure con i moduli è possibile mantenere l'implementazione privata (e quindi organizzarla come si desidera) ed esportare solo interfacce e fabbriche. Un esempio:

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()); 
+0

Ma devo essere in grado di condividere l'implementazione in AbstractEngine attraverso diverse classi concrete, non solo StandardEngine. Mi dispiace di non averlo reso evidente nel mio post. Non vedo come la tua soluzione aiuti a farlo. * edit * Ahh, non importa, la tua menzione della parte relativa ai mixin che porterebbe a condividere il codice tra più classi concrete. Dovrò fare un tentativo. – brycedarling

+0

Dopo aver implementato questo utilizzando i mixin, sono un po 'deluso da quanto codice ho dovuto copiare su entrambe le classi StandardEngine e TurboEngine. Vorrei che ci fosse una parola chiave che si prendesse cura di questo e rimosso la necessità di chiamare applyMixins, o classi astratte, o entrambi. – brycedarling

+0

Inoltre, il metodo toString personalizzato su Engine stampa sempre "Engine" per il nome della classe quando viene implementato come mixin invece di "StandardEngine" o "TurboEngine" come quando si utilizza l'ereditarietà. – brycedarling

0

Quando si chiama il StandardEngine constructor, una chiamata a super(size, false). Questa chiamata alla classe base è ciò che genera il secondo errore "Classe astratta".

Per simulare una classe base astratta che getterà quando istanziato, creare una funzione Init che viene chiamato da classe derivata.

class AbstractEngine implements IEngine { 
    private _size: number; 
    private _turbo: boolean; 
    constructor() { 
    throw "Abstract class"; 
    } 
    init(size:number, turbo: boolean) { 
    this._size = size; 
    this._turbo = turbo; 
    } 
} 

class StandardEngine extends AbstractEngine { 
    constructor(size: number) { 
    // not turbo charged 
    // do not call super here 
    init(size, false); 
    } 
} 
+1

Se Tralascio la chiamata a super in StandardEngine ottengo l'errore "Costruttori di classi derivate devono contenere un 'super' chiamare".Quando provo il tuo codice ho anche l'errore "Impossibile trovare il simbolo 'init'" perché manca il "questo" qualificatore. – brycedarling

0

Una soluzione alternativa potrebbe essere quella di utente una proprietà che se impostato indica che il costruttore viene chiamato da una classe figlia è sicuro di continuare. Questo è mostrato di seguito:

class AbstractEngine { 
    safe; // IMPORTANT : do not initialize 
    constructor(private size: number, private turbo: boolean) { 
    if(!this.safe) throw "Abstract class"; // IMPORTANT 
    }  

} 

class StandardEngine extends AbstractEngine { 
    constructor(size: number) {  
    this.safe = true; // IMPORTANT 
    super(size, false); 
    } 
} 
+0

Quando ho provato questo codice, ho ancora ottenuto l'errore generato per la classe Abstract durante l'istanziazione di un motore standard. Capisco l'idea, ma l'implementazione non sembra funzionare. – brycedarling

+0

@brycedarling Ho corretto l'errore. la classe base non dovrebbe impostare il valore altrimenti il ​​valore figlio verrà sovrascritto :) – basarat

5

A partire da oggi, TypeScript 1.6 is live e ha il supporto per la parola chiave 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 
Problemi correlati