È piuttosto semplice, in realtà:
Invece di avere un costruttore che fa il tuo setup,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... chiedi al tuo costruttore di fare poco o niente e di scrivere un metodo chiamato .init
o .initialize
, che farebbe quello che farebbe normalmente il tuo costruttore.
public class Thing {
public Thing () {}
public void initialize (a, b, c, d) {
this.x = a; /*...*/
}
}
Quindi ora invece di andare semplicemente come:
Thing thing = new Thing(1, 2, 3, 4);
Puoi andare:
Thing thing = new Thing();
thing.doSomething();
thing.bind_events(evt_1, evt_2);
thing.initialize(1, 2, 3, 4);
Il vantaggio è che ora puoi usare la dipendenza da iniezione / inversione di controllo più facilmente nei tuoi sistemi.
Invece di dire
public class Soldier {
private Weapon weapon;
public Soldier (name, x, y) {
this.weapon = new Weapon();
}
}
È possibile costruire il soldato, gli danno un metodo di equipaggiare, dove consegnare l'un'arma, e quindi chiamare tutto il resto delle funzioni di costruzione.
Quindi ora, invece di sottoclassare i nemici in cui un soldato ha una pistola e un altro ha un fucile e un altro ha un fucile, e questa è l'unica differenza, puoi solo dire:
Soldier soldier1 = new Soldier(),
soldier2 = new Soldier(),
soldier3 = new Soldier();
soldier1.equip(new Pistol());
soldier2.equip(new Rifle());
soldier3.equip(new Shotgun());
soldier1.initialize("Bob", 32, 48);
soldier2.initialize("Doug", 57, 200);
soldier3.initialize("Mike", 92, 30);
Lo stesso accordo con la distruzione. Se hai esigenze particolari (rimozione di listener di eventi, rimozione di istanze da array / qualsiasi struttura con cui stai lavorando, ecc.), Le chiameresti manualmente, in modo da sapere esattamente quando e dove nel programma si stava verificando.
MODIFICARE
Come ha sottolineato Kryotan, di seguito, questo risponde al "How" del post originale , ma in realtà non fa un buon lavoro di "Why".
Come probabilmente vedrai nella risposta sopra, potrebbe non esserci molta differenza tra:
var myObj = new Object();
myObj.setPrecondition(1);
myObj.setOtherPrecondition(2);
myObj.init();
e scrivere
var myObj = new Object(1,2);
pur avendo solo una funzione di costruzione più grande.
C'è un argomento da fare per gli oggetti che hanno 15 o 20 pre-condizioni, il che renderebbe molto, molto difficile lavorare con un costruttore, e renderebbe le cose più facili da vedere e ricordare, estraendo quelle cose nell'interfaccia , in modo da poter vedere come funziona l'istanza, un livello superiore.
La configurazione opzionale degli oggetti è un'estensione naturale di ciò; facoltativamente impostare i valori sull'interfaccia, prima di far funzionare l'oggetto.
JS ha alcune scorciatoie fantastiche per questa idea, che sembrano fuori posto in linguaggi di tipo c più forti.
Detto questo, è probabile che, se hai a che fare con un elenco di argomenti così a lungo nel tuo costruttore, che il tuo oggetto è troppo grande e fa troppo, così come è. Ancora una volta, questa è una preferenza personale, e ci sono eccezioni in lungo e in largo, ma se stai passando 20 cose in un oggetto, è probabile che tu possa trovare un modo per fare in modo che quell'oggetto faccia meno, creando oggetti più piccoli .
Un motivo più pertinente e ampiamente applicabile sarebbe che l'inizializzazione di un oggetto si basa su dati asincroni, che attualmente non si possiedono.
Sai che hai bisogno dell'oggetto, quindi lo creerai comunque, ma per farlo funzionare correttamente, ha bisogno di dati dal server o da un altro file che ora deve caricare.
Ancora una volta, sia che tu stia trasferendo i dati necessari in un gigantesco init, o costruendo un'interfaccia non è davvero importante per il concetto, tanto quanto è importante per l'interfaccia del tuo oggetto e il design del tuo sistema ...
Ma in termini di costruzione dell'oggetto, potresti fare qualcosa del genere:
var obj_w_async_dependencies = new Object();
async_loader.load(obj_w_async_dependencies.async_data, obj_w_async_dependencies);
async_loader
potrebbe essere passato un nome di file, o un nome di risorsa o altro, caricare quella risorsa - forse carica file audio o dati di immagine o forse carica le statistiche dei personaggi salvate ...
... e poi reinserirebbe quei dati obj_w_async_dependencies.init(result);
.
Questo tipo di dinamica si trova frequentemente nelle app Web.
Non necessariamente nella costruzione di un oggetto, per applicazioni di livello superiore: ad esempio, le gallerie potrebbero caricarsi e inizializzarsi immediatamente e quindi visualizzare le foto durante lo streaming - non è in realtà un'inizializzazione asincrona, ma dove sarebbe visto più frequentemente sarebbe nelle librerie JavaScript.
Un modulo potrebbe dipendere da un altro, quindi l'inizializzazione di quel modulo potrebbe essere rinviata fino al completamento del caricamento delle persone a carico.
In termini di istanze specifiche del gioco, considera una Game
classe reale .
Perché non possiamo chiamare .start
o .run
nel costruttore?
Le risorse devono essere caricate: il resto di tutto è stato praticamente definito e va bene, ma se proviamo a eseguire il gioco senza una connessione al database, o senza trame o modelli o suoni o livelli, non sarà un gioco particolarmente interessante ...
... quindi qual è, allora, la differenza tra ciò che vediamo di un tipico Game
, tranne per il fatto che diamo al suo metodo "vai avanti" un nome che è più interessante di .init
(o al contrario, spezziamo ulteriormente l'inizializzazione, per separare il caricamento, impostare le cose che sono state caricate ed eseguire il programma quando tutto è stato impostato).