Eigentlich ist es ziemlich einfach:
Anstatt einen Konstruktor zu haben, der Ihr Setup ausführt,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... lassen Sie Ihren Konstruktor wenig oder gar nichts tun und schreiben Sie eine Methode namens .init
oder .initialize
, die das tut, was Ihr Konstruktor normalerweise tun würde.
public class Thing {
public Thing () {}
public void initialize (a, b, c, d) {
this.x = a; /*...*/
}
}
Also jetzt anstatt einfach so zu gehen wie:
Thing thing = new Thing(1, 2, 3, 4);
Du kannst gehen:
Thing thing = new Thing();
thing.doSomething();
thing.bind_events(evt_1, evt_2);
thing.initialize(1, 2, 3, 4);
Der Vorteil besteht darin, dass Sie die Abhängigkeitsinjektion / -inversion der Steuerung jetzt einfacher in Ihren Systemen verwenden können.
Anstatt zu sagen
public class Soldier {
private Weapon weapon;
public Soldier (name, x, y) {
this.weapon = new Weapon();
}
}
Sie können den Soldaten bauen, geben Sie ihm ein Ausrüsten Verfahren, in dem Sie die Hand ihm eine Waffe, und dann der ganze Rest der Konstruktor - Funktionen aufrufen.
Anstatt also Feinde zu unterordnen, bei denen ein Soldat eine Pistole und ein anderer ein Gewehr und ein anderer eine Schrotflinte hat, und das ist der einzige Unterschied, können Sie einfach sagen:
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);
Gleiches gilt für die Zerstörung. Wenn Sie spezielle Anforderungen haben (Entfernen von Ereignis-Listenern, Entfernen von Instanzen aus Arrays / Strukturen, mit denen Sie arbeiten usw.), rufen Sie diese manuell auf, damit Sie genau wissen, wann und wo in dem Programm, das gerade ausgeführt wurde.
BEARBEITEN
Wie Kryotan weiter unten ausgeführt hat, beantwortet dies das "Wie" des ursprünglichen Beitrags , macht aber kein wirklich gutes "Warum".
Wie Sie wahrscheinlich in der obigen Antwort sehen können, gibt es möglicherweise keinen großen Unterschied zwischen:
var myObj = new Object();
myObj.setPrecondition(1);
myObj.setOtherPrecondition(2);
myObj.init();
und schreiben
var myObj = new Object(1,2);
während nur eine größere Konstruktorfunktion haben.
Es gibt ein Argument für Objekte mit 15 oder 20 Vorbedingungen, die es einem Konstruktor sehr, sehr schwer machen würden, damit zu arbeiten, und es würde das Sehen und Erinnern erleichtern, indem diese Dinge in die Schnittstelle gezogen werden , damit Sie sehen können, wie die Instanziierung funktioniert, eine Ebene höher.
Die optionale Konfiguration von Objekten ist eine natürliche Erweiterung davon. Optional können Sie Werte auf der Schnittstelle festlegen, bevor das Objekt ausgeführt wird.
JS hat einige großartige Abkürzungen für diese Idee, die in stärker typisierten c-ähnlichen Sprachen einfach fehl am Platz zu sein scheinen.
Das heißt, wenn Sie es mit einer so langen Argumentliste in Ihrem Konstruktor zu tun haben, besteht die Möglichkeit, dass Ihr Objekt zu groß ist und so viel tut, wie es ist. Auch dies ist eine persönliche Präferenzsache, und es gibt weit und breit Ausnahmen. Wenn Sie jedoch 20 Dinge an ein Objekt übergeben, stehen die Chancen gut, dass Sie einen Weg finden, dieses Objekt weniger zu machen, indem Sie kleinere Objekte erstellen .
Ein relevanterer und allgemein anwendbarer Grund wäre, dass die Initialisierung eines Objekts auf asynchronen Daten beruht, über die Sie derzeit nicht verfügen.
Sie wissen, dass Sie das Objekt benötigen, also werden Sie es trotzdem erstellen, aber damit es ordnungsgemäß funktioniert, benötigt es Daten vom Server oder von einer anderen Datei, die es jetzt laden muss.
Auch hier ist es für das Konzept nicht wirklich wichtig, ob Sie die benötigten Daten an einen gigantischen Init übergeben oder eine Schnittstelle aufbauen, sondern für die Schnittstelle Ihres Objekts und das Design Ihres Systems ...
In Bezug auf das Erstellen des Objekts können Sie jedoch Folgendes tun:
var obj_w_async_dependencies = new Object();
async_loader.load(obj_w_async_dependencies.async_data, obj_w_async_dependencies);
async_loader
Möglicherweise wird ein Dateiname oder ein Ressourcenname oder was auch immer übergeben. Laden Sie diese Ressource - möglicherweise werden Audiodateien oder Bilddaten geladen, oder es werden gespeicherte Zeichenstatistiken geladen ...
... und dann würde es diese Daten zurückspeisen obj_w_async_dependencies.init(result);
.
Diese Art von Dynamik findet man häufig in Web-Apps.
Nicht unbedingt in der Konstruktion eines Objekts, für Anwendungen auf höherer Ebene: Beispielsweise können Galerien sofort geladen und initialisiert werden und dann Fotos anzeigen, während sie einströmen - das ist eigentlich keine asynchrone Initialisierung, aber dort, wo sie häufiger zu sehen ist in JavaScript-Bibliotheken.
Ein Modul hängt möglicherweise von einem anderen ab, sodass die Initialisierung dieses Moduls möglicherweise verschoben wird, bis das Laden der abhängigen Elemente abgeschlossen ist.
Betrachten Sie in Bezug auf spielspezifische Instanzen eine tatsächliche Game
Klasse.
Warum können wir nicht .start
oder .run
im Konstruktor aufrufen ?
Ressourcen müssen geladen werden - der Rest von allem wurde so ziemlich definiert und ist in Ordnung, aber wenn wir versuchen, das Spiel ohne Datenbankverbindung oder ohne Texturen oder Modelle oder Sounds oder Levels auszuführen, wird es nicht so sein ein besonders interessantes Spiel ...
... na und, dann ist der Unterschied zwischen dem, was wir von einem typischen sehen Game
, außer dass wir seiner "Go-Ahead" -Methode einen Namen geben, der interessanter ist als .init
(oder umgekehrt, die Initialisierung noch weiter auseinander zu brechen, um das Laden zu trennen). Einrichten der geladenen Dinge und Ausführen des Programms, wenn alles eingerichtet wurde).