Dattiloscritto: come estendere due classi?

Voglio risparmiare tempo e riutilizzare codice comune tra le classi che estende le classi PIXI (una libreria di rendering WebGl 2d).

Interfacce Oggetto:

module Game.Core { export interface IObject {} export interface IManagedObject extends IObject{ getKeyInManager(key: string): string; setKeyInManager(key: string): IObject; } } 

Il mio problema è che il codice all’interno di getKeyInManager e setKeyInManager non cambierà e voglio riutilizzarlo, non per duplicarlo, ecco l’implementazione:

 export class ObjectThatShouldAlsoBeExtended{ private _keyInManager: string; public getKeyInManager(key: string): string{ return this._keyInManager; } public setKeyInManager(key: string): DisplayObject{ this._keyInManager = key; return this; } } 

Quello che voglio fare è aggiungere automaticamente, attraverso un Manager.add() , la chiave utilizzata nel gestore per fare riferimento all’object all’interno dell’object stesso nella sua proprietà _keyInManager .

Quindi, facciamo un esempio con una Texture. Qui va il TextureManager

 module Game.Managers { export class TextureManager extends Game.Managers.Manager { public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{ return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name); } } } 

Quando faccio this.add() , voglio il Game.Managers.Manager per chiamare un metodo che esisterebbe sull’object restituito da Game.Core.Texture.fromImage("/" + relativePath) . Questo object, in questo caso, sarebbe una Texture :

 module Game.Core { // I must extends PIXI.Texture, but I need to inject the methods in IManagedObject. export class Texture extends PIXI.Texture { } } 

So che IManagedObject è un’interfaccia e non può contenere l’implementazione, ma non so cosa scrivere per iniettare la class ObjectThatShouldAlsoBeExtended all’interno della mia class Texture . Sapendo che sarebbe stato richiesto lo stesso processo per Sprite , TilingSprite , Layer e altro.

Ho bisogno di un feedback / consigli TypeScript esperti qui, deve essere ansible farlo, ma non da più estensioni poiché solo uno è ansible al momento, non ho trovato alcuna altra soluzione.

C’è una caratteristica poco conosciuta in TypeScript che ti permette di usare Mixins per creare piccoli oggetti riutilizzabili. È ansible comporli in oggetti più grandi utilizzando l’ereditarietà multipla (l’ereditarietà multipla non è consentita per le classi, ma è consentita per i mixin, che sono come interfacce con una implenentazione associata).

Maggiori informazioni su Mixins TypeScript

Penso che potresti usare questa tecnica per condividere componenti comuni tra molte classi del tuo gioco e riutilizzare molti di questi componenti da una singola class nel tuo gioco:

Ecco una rapida demo di Mixins … in primo luogo, i sapori che vuoi mixare:

 class CanEat { public eat() { alert('Munch Munch.'); } } class CanSleep { sleep() { alert('Zzzzzzz.'); } } 

Quindi il metodo magico per la creazione di Mixin (ne hai bisogno solo una volta da qualche parte nel tuo programma …)

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

E poi puoi creare classi con ereditarietà multipla dai sapori mixin:

 class Being implements CanEat, CanSleep { eat: () => void; sleep: () => void; } applyMixins (Being, [CanEat, CanSleep]); 

Si noti che non esiste un’implementazione effettiva in questa class, quanto basta per farla superare i requisiti delle “interfacce”. Ma quando usiamo questa class, tutto funziona.

 var being = new Being(); // Zzzzzzz... being.sleep(); 

Vorrei suggerire di utilizzare il nuovo approccio mixins descritto qui: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/

Questo approccio è migliore, rispetto all’approccio “applyMixins” descritto da Fenton, perché il programma di compilazione automatica ti aiuterà e mostrerà tutti i metodi / proprietà dalle classi di base e di seconda ereditarietà.

Questo approccio potrebbe essere controllato sul sito TS Playground .

Ecco l’implementazione:

 class MainClass { testMainClass() { alert("testMainClass"); } } const addSecondInheritance = (BaseClass: { new(...args) }) => { return class extends BaseClass { testSecondInheritance() { alert("testSecondInheritance"); } } } // Prepare the new class, which "inherits" 2 classs (MainClass and the cass declared in the addSecondInheritance method) const SecondInheritanceClass = addSecondInheritance(MainClass); // Create object from the new prepared class const secondInheritanceObj = new SecondInheritanceClass(); secondInheritanceObj.testMainClass(); secondInheritanceObj.testSecondInheritance(); 

Sfortunatamente il typescript non supporta l’ereditarietà multipla. Quindi non c’è una risposta completamente banale, probabilmente dovrai ristrutturare il tuo programma

Ecco alcuni suggerimenti:

  • Se questa class addizionale contiene un comportamento condiviso da molte delle sottoclassi, ha senso inserirlo nella gerarchia delle classi, da qualche parte in alto. Forse potresti ricavare la comune superclass di Sprite, Texture, Layer, … da questa class? Questa sarebbe una buona scelta, se riuscirai a trovare un buon posto nell’irarchia di tipo. Ma non raccomanderei di inserire questa class in un punto casuale. L’ereditarietà esprime un “è una relazione”, ad esempio un cane è un animale, una trama è un’istanza di questa class. Dovresti chiederti se questo davvero modella la relazione tra gli oggetti nel tuo codice. Un albero di ereditarietà logica è molto prezioso

  • Se la class aggiuntiva non si adatta logicamente alla gerarchia di tipi, è ansible utilizzare l’aggregazione. Ciò significa che aggiungi una variabile di istanza del tipo di questa class a una superclass comune di Sprite, Texture, Layer, … Quindi puoi accedere alla variabile con il suo getter / setter in tutte le sottoclassi. Questo modella un “Ha una – relazione”.

  • Puoi anche convertire la tua class in un’interfaccia. Quindi potresti estendere l’interfaccia con tutte le tue classi, ma dovresti implementare correttamente i metodi in ogni class. Ciò significa una certa ridondanza del codice, ma in questo caso non molto.

Devi decidere da solo quale approccio ti piace di più. Personalmente raccomanderei di convertire la class in un’interfaccia.

Un consiglio: Typescript offre proprietà, che sono zucchero sintattico per getter e setter. Potresti dare un’occhiata a questo: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/