Dependencies are injected in constructor but it is not recommended to use them.
Acho que o autor está dizendo o oposto disso com foco no acoplamento fraco. Ele citou dizendo:
"The Constructor Injection design pattern is a extremely useful way to implement loose coupling"
O acoplamento frouxo é o núcleo do bom design de software. Torna as classes reutilizáveis, extensíveis e testáveis.
Initialize phase brings complexity and should be avoided.
Sim, mas isso não significa que sempre possa ser evitado. Os construtores não têm um valor de retorno e nem todos os idiomas suportam o lançamento de exceções. Alguns livros argumentam que um construtor deve ser sempre bem-sucedido e eu concordo com essa regra.
Aqui estão alguns exemplos de código fonte de ambos os estilos. Ambas as abordagens funcionam, mas qual é a melhor?
try
{
FileReader f = new FileReader("something.txt");
String str = f.read();
}
catch(FileNotFound e) {...}
ou
FileReader f = new FileReader("something.txt");
if(f.exists())
{
String str = f.read();
}
O primeiro exemplo tem o objeto FileReader
dependente de um recurso externo. Se não puder acessá-lo, ele falhará durante o construtor. Isso cria dependência além do controlador do programador e também teste de unidade.
No segundo exemplo, não há dependência. O objeto pode ser criado mesmo se o recurso não existir. A dependência agora é de responsabilidade do programador.
Então, como isso se relaciona com initializing
de um objeto. É muito simples. Quem deve ser responsável? O objeto ou o programador. Isso é com o autor do objeto. Ele / ela pode ter boas razões para desistir do controle da construção do objeto para o programador que o utiliza.
Se initializing
de um objeto for uma tarefa complexa, você localizará esse código em uma classe de fábrica para ter um local para fazer alterações.
Are not methods like Connection.Open() just another name for Initialize?
O método Connection.Open()
é um inicializador apenas se Connection.Read()
falhar se Open()
não for chamado.
Aqui está o problema que o autor está falando.
Connection con = new Connection();
con->Read(); // this will fail, Open() was not called
Para corrigir o código acima. Você tem que escrever isso, e isso é um projeto ruim.
Connection con = new Connection();
con->Open("192.168.1.1"); // bug fix, forgot to call Open()
con->Read();
Eu li muitos comentários no código-fonte de programadores que escrevem "correção de bug, esqueci de chamar X (...)". O argumento é que o bug era evitável em primeiro lugar. O autor da classe Connection
não usou um inicializador.
Aqui está a solução para o problema.
Connection con = new Connection("192.168.1.1");
con->Read();
Agora, como você lida com uma conexão com falha é respondido mais acima na minha resposta. O construtor lança uma exceção ou o programador deve chamar isOpen()
antes de read()
.
So can anyone describe a good initialization pattern in the context of Dependency Injection that addresses the concerns Mark Seeman brings up?
Pode ser difícil de entender, mas a resposta está no Single Responsibility Principle
.
Para o meu exemplo com o objeto Connection
. Ele quebrou a regra do SRP. O objeto Connection
é aberto e lido a partir do recurso. São duas responsabilidades diferentes. Podemos consertar isso fragmentando o objeto em várias partes, cada uma com suas próprias responsabilidades.
Aqui está um exemplo;
try
{
SocketAddress addr = new SocketAddress("192.168.1.1");
try
{
Socket s = new Socket(addr);
try
{
SocketReader r = new SocketReader(s);
if(r != null)
{
String str = r->Read();
}
} catch(ReadFailure e) {..}
} catch(ConnectionFailure e) {..}
} catch(BadAddress e) {..}
Cada objeto é responsável apenas por uma coisa.
-
SocketAddress
só será construído com sucesso se o endereço for válido.
-
Socket
só será construído se puder estabelecer uma conexão com o endereço.
-
SocketReader
só funciona se puder ler.
Como você pode ver. Você tem que escrever muito mais código-fonte, e é por isso que muitas vezes vemos a injeção de dependência evitada. É um trabalho extra por parte do programador.