É bem simples, na verdade:
Em vez de ter um construtor que faz sua configuração,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... peça ao seu construtor que faça pouco ou nada e escreva um método chamado .init
or .initialize
, que faria o que seu construtor faria normalmente.
public class Thing {
public Thing () {}
public void initialize (a, b, c, d) {
this.x = a; /*...*/
}
}
Então agora, em vez de apenas ir como:
Thing thing = new Thing(1, 2, 3, 4);
Você pode ir:
Thing thing = new Thing();
thing.doSomething();
thing.bind_events(evt_1, evt_2);
thing.initialize(1, 2, 3, 4);
O benefício é que agora você pode usar injeção de dependência / inversão de controle com mais facilidade em seus sistemas.
Ao invés de dizer
public class Soldier {
private Weapon weapon;
public Soldier (name, x, y) {
this.weapon = new Weapon();
}
}
Você pode construir o soldado, dar a ele um método de equipar, onde você entrega uma arma para ele, e ENTÃO chama todas as demais funções do construtor.
Então agora, em vez de subclassificar inimigos em que um soldado tem uma pistola e outro tem um rifle e outro tem uma espingarda, e essa é a única diferença, você pode apenas dizer:
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);
O mesmo acordo com a destruição. Se você tiver necessidades especiais (remover ouvintes de eventos, remover instâncias de matrizes / quaisquer estruturas com as quais estiver trabalhando, etc.), você as chamaria manualmente, para saber exatamente quando e onde no programa que estava acontecendo.
EDITAR
Como Kryotan apontou, abaixo, isso responde às perguntas originais "Como" , mas na verdade não faz um bom trabalho com o "Por que".
Como você provavelmente pode ver na resposta acima, pode não haver muita diferença entre:
var myObj = new Object();
myObj.setPrecondition(1);
myObj.setOtherPrecondition(2);
myObj.init();
e escrever
var myObj = new Object(1,2);
enquanto apenas tendo uma função construtora maior.
Há um argumento a ser feito para objetos com 15 ou 20 pré-condições, o que tornaria um construtor muito, muito difícil de trabalhar, e facilitaria as coisas para ver e lembrar, puxando essas coisas para a interface , para que você possa ver como a instanciação funciona, um nível acima.
A configuração opcional de objetos é uma extensão natural disso; opcionalmente, definindo valores na interface, antes de executar o objeto.
O JS possui alguns ótimos atalhos para essa idéia, que parecem deslocados em linguagens do tipo c de tipo mais forte.
Dito isso, as chances são de que, se você estiver lidando com uma lista de argumentos tão longa em seu construtor, que seu objeto seja muito grande e faça muito, como é. Novamente, isso é uma coisa de preferência pessoal, e há exceções amplamente, mas se você estiver passando 20 coisas para um objeto, é provável que você encontre uma maneira de fazer com que esse objeto faça menos, criando objetos menores .
Um motivo mais pertinente e amplamente aplicável seria o fato de a inicialização de um objeto depender de dados assíncronos, que você não possui atualmente.
Você sabe que precisa do objeto e, portanto, irá criá-lo de qualquer maneira, mas para que ele funcione corretamente, ele precisa de dados do servidor ou de outro arquivo que agora precisa carregar.
Novamente, se você está passando os dados necessários para um init gigantesco ou construindo uma interface não é realmente importante para o conceito, é importante para a interface do seu objeto e o design do seu sistema ...
Mas em termos de construção do objeto, você pode fazer algo assim:
var obj_w_async_dependencies = new Object();
async_loader.load(obj_w_async_dependencies.async_data, obj_w_async_dependencies);
async_loader
pode receber um nome de arquivo ou um nome de recurso ou qualquer outra coisa, carregar esse recurso - talvez carregue arquivos de som ou dados de imagem ou carregue estatísticas de caracteres salvas ...
... e então alimentaria esses dados novamente obj_w_async_dependencies.init(result);
.
Esse tipo de dinâmica é encontrado com frequência em aplicativos da web.
Não necessariamente na construção de um objeto, para aplicativos de nível superior: por exemplo, as galerias podem carregar e inicializar imediatamente e, em seguida, exibir fotos à medida que entram - isso não é realmente uma inicialização assíncrona, mas onde é visto com mais frequência seria nas bibliotecas JavaScript.
Um módulo pode depender de outro e, portanto, a inicialização desse módulo pode ser adiada até que o carregamento dos dependentes seja concluído.
Em termos de instâncias específicas do jogo, considere uma Game
classe real .
Por que não podemos chamar .start
ou .run
no construtor?
Os recursos precisam ser carregados - o resto de tudo já foi definido e está pronto, mas se tentarmos executar o jogo sem uma conexão com o banco de dados, ou sem texturas, modelos, sons ou níveis, não será possível. um jogo particularmente interessante ...
... então qual é a diferença entre o que vemos de um típico Game
, exceto pelo fato de atribuirmos ao método "ir em frente" um nome mais interessante do que .init
(ou, inversamente, separar ainda mais a inicialização, para separar o carregamento, configurar as coisas que foram carregadas e executar o programa quando tudo estiver configurado).