Dependencies are injected in constructor but it is not recommended to use them.
Penso che l'autore stia dicendo il contrario di ciò che si concentra sull'accoppiamento lento. Ha detto:
"The Constructor Injection design pattern is a extremely useful way to implement loose coupling"
L'accoppiamento lento è il fulcro del buon design del software. Rende le classi riutilizzabili, estendibili e verificabili.
Initialize phase brings complexity and should be avoided.
Sì, ma ciò non significa che possa sempre essere evitato. I costruttori non hanno un valore di ritorno e non tutte le lingue supportano il lancio di eccezioni. Alcuni libri sostengono che un costruttore dovrebbe sempre avere successo e sono d'accordo con questa regola.
Ecco alcuni esempi di codice sorgente di entrambi gli stili. Entrambi gli approcci funzionano, ma quale è meglio?
try
{
FileReader f = new FileReader("something.txt");
String str = f.read();
}
catch(FileNotFound e) {...}
o
FileReader f = new FileReader("something.txt");
if(f.exists())
{
String str = f.read();
}
Il primo esempio ha l'oggetto FileReader
dipendente da una risorsa esterna. Se non può accedervi, fallisce durante il costruttore. Ciò crea dipendenza oltre al controller del programmatore e anche test delle unità.
Nel secondo esempio non c'è dipendenza. L'oggetto può essere creato anche se la risorsa non esiste. La dipendenza ora dipende dal programmatore da applicare.
Quindi, in che modo questo si riferisce a initializing
di un oggetto. È molto semplice. Chi dovrebbe essere responsabile? L'oggetto o il programmatore. Questo dipende dall'autore dell'oggetto. Lui / lei potrebbe avere delle buone ragioni per rinunciare al controllo della costruzione dell'oggetto per il programmatore che lo usa.
Se initializing
un oggetto è un'attività complessa, quindi localizzi quel codice in una classe factory in modo da avere un posto dove andare per apportare modifiche.
Are not methods like Connection.Open() just another name for Initialize?
Il metodo Connection.Open()
è un inizializzatore solo se Connection.Read()
fallisce se Open()
non è stato chiamato.
Ecco il problema di cui parla l'autore.
Connection con = new Connection();
con->Read(); // this will fail, Open() was not called
Per correggere il codice precedente. Devi scrivere questo, e questo è un cattivo progetto.
Connection con = new Connection();
con->Open("192.168.1.1"); // bug fix, forgot to call Open()
con->Read();
Ho letto molti commenti nel codice sorgente da programmatori che scrivono "bug fix, ho dimenticato di chiamare X (...)". L'argomento è che il bug era evitabile in primo luogo. Se l'autore della classe Connection
non ha utilizzato un inizializzatore.
Ecco la soluzione al problema.
Connection con = new Connection("192.168.1.1");
con->Read();
Ora, come si gestisce una connessione fallita viene risposto più in alto nella mia risposta. O il costruttore lancia un'eccezione o il programmatore deve chiamare isOpen()
prima di read()
.
So can anyone describe a good initialization pattern in the context of Dependency Injection that addresses the concerns Mark Seeman brings up?
Potrebbe essere difficile da capire, ma la risposta è in Single Responsibility Principle
.
Per il mio esempio con l'oggetto Connection
. Ha infranto la regola SRP. L'oggetto Connection
si apre e legge dalla risorsa. Sono due diverse responsabilità. Possiamo risolvere questo problema fratturando l'oggetto in più pezzi ciascuno con le proprie responsabilità.
Ecco un esempio;
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) {..}
Ogni oggetto è responsabile solo di una cosa.
-
SocketAddress
verrà costruito correttamente solo se l'indirizzo è valido.
-
Socket
verrà costruito solo se può effettuare una connessione all'indirizzo
-
SocketReader
funziona solo se può leggere.
Come puoi vedere. Devi scrivere molto più codice sorgente, e questo è il motivo per cui spesso vediamo l'iniezione della dipendenza evitata. È un lavoro extra da parte del programmatore.