Es bastante simple, en realidad:
En lugar de tener un Constructor que hace tu configuración,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... haga que su constructor haga poco o nada, y escriba un método llamado .init
o .initialize
, que haría lo que su constructor haría normalmente.
public class Thing {
public Thing () {}
public void initialize (a, b, c, d) {
this.x = a; /*...*/
}
}
Entonces, en lugar de simplemente decir:
Thing thing = new Thing(1, 2, 3, 4);
Se puede ir:
Thing thing = new Thing();
thing.doSomething();
thing.bind_events(evt_1, evt_2);
thing.initialize(1, 2, 3, 4);
El beneficio es que ahora puede usar la inyección de dependencia / inversión de control más fácilmente en sus sistemas.
En lugar de decir
public class Soldier {
private Weapon weapon;
public Soldier (name, x, y) {
this.weapon = new Weapon();
}
}
Se puede construir el soldado, le dan un método Equip, donde se entregue él un arma, y luego llamar a todo el resto de las funciones constructoras.
Así que ahora, en lugar de subclasificar enemigos donde un soldado tiene una pistola y otro tiene un rifle y otro tiene una escopeta, y esa es la única diferencia, solo puede decir:
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);
El mismo trato con la destrucción. Si tiene necesidades especiales (eliminar oyentes de eventos, eliminar instancias de matrices / cualquier estructura con la que esté trabajando, etc.), debería llamarlas manualmente, para que sepa exactamente cuándo y dónde en el programa que estaba sucediendo.
EDITAR
Como Kryotan ha señalado, a continuación, esto responde al "Cómo" de la publicación original , pero en realidad no hace un buen trabajo de "Por qué".
Como probablemente pueda ver en la respuesta anterior, puede que no haya mucha diferencia entre:
var myObj = new Object();
myObj.setPrecondition(1);
myObj.setOtherPrecondition(2);
myObj.init();
y escribiendo
var myObj = new Object(1,2);
mientras que solo tiene una función constructora más grande.
Hay un argumento para los objetos que tienen 15 o 20 condiciones previas, lo que haría que un constructor sea muy, muy difícil de trabajar, y haría que las cosas fueran más fáciles de ver y recordar, sacando esas cosas a la interfaz , para que pueda ver cómo funciona la instanciación, un nivel más alto.
La configuración opcional de objetos es una extensión natural de esto; opcionalmente, establecer valores en la interfaz, antes de ejecutar el objeto.
JS tiene algunos atajos geniales para esta idea, que parecen fuera de lugar en lenguajes tipo C de tipo más fuerte.
Dicho esto, lo más probable es que, si está lidiando con una lista de argumentos tan larga en su constructor, que su objeto es demasiado grande y hace demasiado, como es. Una vez más, esto es algo de preferencia personal, y hay excepciones a lo largo y ancho, pero si está pasando 20 cosas en un objeto, es muy probable que encuentre una manera de hacer que ese objeto haga menos, haciendo objetos más pequeños. .
Una razón más pertinente, y que es ampliamente aplicable, sería que la inicialización de un objeto se basa en datos asincrónicos, que actualmente no tiene.
Sabe que necesita el objeto, por lo que lo va a crear de todos modos, pero para que funcione correctamente, necesita datos del servidor o de otro archivo que ahora necesita cargar.
Una vez más, ya sea que esté pasando los datos necesarios a un gigantesco init, o construyendo una interfaz, no es realmente importante para el concepto, tanto como es importante para la interfaz de su objeto y el diseño de su sistema ...
Pero en términos de construir el objeto, podrías hacer algo como esto:
var obj_w_async_dependencies = new Object();
async_loader.load(obj_w_async_dependencies.async_data, obj_w_async_dependencies);
async_loader
podría pasar un nombre de archivo, o un nombre de recurso o lo que sea, cargar ese recurso; tal vez carga archivos de sonido o datos de imagen, o tal vez carga estadísticas de caracteres guardadas ...
... y luego volvería a alimentar esos datos obj_w_async_dependencies.init(result);
.
Este tipo de dinámica se encuentra con frecuencia en las aplicaciones web.
No necesariamente en la construcción de un objeto, para aplicaciones de nivel superior: por ejemplo, las galerías pueden cargarse e inicializarse de inmediato, y luego mostrar fotos a medida que se transmiten, eso no es realmente una inicialización asincrónica, pero donde se ve con más frecuencia sería en bibliotecas de JavaScript.
Un módulo puede depender de otro, por lo que la inicialización de ese módulo puede diferirse hasta que se complete la carga de los dependientes.
En términos de instancias específicas de este juego, considere una Game
clase real .
¿Por qué no podemos llamar .start
o .run
en el constructor?
Deben cargarse recursos: el resto de todo se ha definido y está listo, pero si intentamos ejecutar el juego sin una conexión de base de datos, o sin texturas, modelos, sonidos o niveles, no será un juego particularmente interesante ...
... entonces, ¿cuál es la diferencia entre lo que vemos de un típico Game
, excepto que le damos a su método "seguir adelante" un nombre que es más interesante que .init
(o por el contrario, separa la inicialización aún más, para separar la carga, configurar las cosas que se han cargado y ejecutar el programa cuando todo se ha configurado).