Esiste un tipo di regola nella letteratura sull'iniezione delle dipendenze, affermando che dovremmo dichiarare tutti gli argomenti nel costruttore, al fine di ottenere un'iniezione del costruttore, che è migliore di altri approcci.
All'inizio, non c'è niente di sbagliato in questo.
Ma mi sono reso conto che questa "regola" si applica solo al regno di - diciamo - controller MVC. Poiché un controller viene generato per eseguire un determinato lavoro, non si discosta dal suo scopo e quindi dispone. In generale, si applica a un "sistema basato su richiesta". Ora, diciamo che abbiamo un blocco che riceve i dati, li trasforma e restituisce i dati trasformati. Questo blocco ha vari altri parametri che devono essere configurati, ad esempio IConditionStrategy (uguale a, GreaterThan ecc.).
Configuriamo il blocco ed eseguiamo l'applicazione. Quindi, vogliamo ottenere altri risultati e quindi cambiamo i parametri di quel blocco (un buon esempio di questo è simulink di matlab). Il blocco non dispone, esiste in uno spazio di lavoro. Ha svolto il suo lavoro (trasformando i dati) e attende nell'area di lavoro per eseguire nuovamente un altro lavoro. Sarebbe molto costoso (per non dire complicato) ricreare tutti i possibili blocchi dello spazio di lavoro. Ma quando iniziamo a cambiare i suoi parametri, che possono variare dai numeri alle varie strategie, dobbiamo fornire in codice dietro alcune implementazioni concrete a questo blocco che ha bisogno della sua interfaccia. In questo caso, come si adatterebbe l'iniezione della dipendenza del costruttore "standard"?
Sfortunatamente, dobbiamo liberarci del costruttore, perché dobbiamo modificare i parametri in fase di esecuzione. Ma poi di nuovo, se ci liberiamo del costruttore, non saremo in grado di garantire che un parametro sia sempre valido. Dovremmo fornire un tipo di implementazione predefinita? (Cadremo nella trappola del localizzatore di servizi!)
E un'altra cosa mi ha sempre infastidito. Capisco il pericolo di un null che appare in fase di esecuzione (quindi dovremmo avere un'iniezione del costruttore), ma cosa otteniamo effettivamente quando creiamo un oggetto e poi lo vincoliamo dal modificare le sue strategie interne, i parametri ecc. Dopo la sua creazione. Una risposta a questo potrebbe non essere necessario cambiarla affatto, basta eliminarla e crearne un'altra. Certo, ma non vedo come questa tecnica si adatti a domini diversi dai "sistemi basati su richieste", come le app web.
Immagina di avere una rete neurale con neuroni che hanno una strategia interna, ad esempio cambiano le loro funzioni di trasferimento in fase di esecuzione anche mentre è in corso l'identificazione o l'apprendimento. Non possiamo disporre un neurone e crearne un altro con la strategia interna desiderata solo per mantenere l'iniezione del costruttore. Ovviamente, in vari libri sulle iniezioni di dipendenza, sono menzionati altri tipi di iniezione, come l'iniezione di metodo, ma ho avuto la sensazione che l'iniezione del costruttore dovrebbe essere usata nell'80% delle occasioni secondo la letteratura. Date le varie applicazioni, la mia stima è che deve essere inferiore all'80%. Sto fraintendendo qualcosa? Gradirei molto le vostre opinioni in merito.
Ad esempio, fornisco il seguente frammento. L'oggetto deve essere modificato in fase di esecuzione, ma l'iniezione del costruttore lo vincola.
public class Transformation {
private readonly IConditionStrategy _condition;
private readonly ISource _input;
public Transformation(IConditionStrategy condition, ISource input) {
this._condition = condition;
this._input = input;
}
public ISource Transform() {
ISource source = new Source();
foreach(var i in this._input) {
if(this._condition.Check(i)){
source.Add(i);
}
}
return source;
}
}
Nell'esempio seguente cambiamo i parametri in fase di esecuzione. Poiché non possiamo semplicemente disporre e ricreare un altro oggetto con i parametri desiderati, non usiamo la pratica classica dell'iniezione di dipendenza.
public class Transformation{
public IConditionStrategy Condition{get;set;}
public ISource Input{get;set;}
public Transformation() {
// we could provide some default
// implementations just to avoid null
}
public ISource Transform(){
ISource source = new Source();
foreach(var i in this._input){
if(this._condition.Check(i)){
source.Add(i);
}
}
return source;
}
}