Il modello di strategia e l'inserimento delle dipendenze ci consentono entrambi di impostare / iniettare oggetti in fase di esecuzione. Qual è la differenza tra il pattern della strategia e l'inserimento delle dipendenze?
Il modello di strategia e l'inserimento delle dipendenze ci consentono entrambi di impostare / iniettare oggetti in fase di esecuzione. Qual è la differenza tra il pattern della strategia e l'inserimento delle dipendenze?
Risposte:
DI e strategia funzionano allo stesso modo, ma la strategia viene utilizzata per dipendenze più dettagliate e di breve durata.
Quando un oggetto è configurato con una Strategia "fissa", ad esempio quando l'oggetto è costruito, la distinzione tra Strategia e DI si offusca. Ma in uno scenario DI è più insolito che le dipendenze degli oggetti cambino durante la loro vita, mentre questo non è raro con Strategy.
Inoltre, è possibile passare strategie come argomenti ai metodi, mentre il concetto correlato di iniezione di argomenti del metodo non è molto diffuso e viene utilizzato principalmente nel contesto dei test automatici.
La strategia si concentra sull'intento e ti incoraggia a creare un'interfaccia con diverse implementazioni che obbediscono allo stesso contratto comportamentale. DI è più solo avere un'implementazione di un comportamento e fornirlo.
Con DI puoi scomporre il tuo programma per altri motivi oltre a essere in grado di scambiare parti dell'implementazione. Un'interfaccia utilizzata in DI con una sola implementazione è molto comune. Una "Strategia" con una sola implementazione concreta (mai) non è un vero problema ma è probabilmente più vicina alla DI.
in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
La differenza è ciò che stanno cercando di ottenere. Il pattern Strategy viene utilizzato in situazioni in cui sai di voler sostituire le implementazioni. Ad esempio, potresti voler formattare i dati in modi diversi: potresti usare il modello di strategia per sostituire un formattatore XML o un formattatore CSV, ecc.
L'inserimento delle dipendenze è diverso in quanto l'utente non sta cercando di modificare il comportamento di runtime. Seguendo l'esempio precedente, potremmo creare un programma di esportazione XML che utilizza un formattatore XML. Piuttosto che strutturare il codice in questo modo:
public class DataExporter() {
XMLFormatter formatter = new XMLFormatter();
}
dovresti 'iniettare' il formattatore nel costruttore:
public class DataExporter {
IFormatter formatter = null;
public DataExporter(IDataFormatter dataFormatter) {
this.formatter = dataFormatter;
}
}
DataExporter exporter = new DataExporter(new XMLFormatter());
Ci sono alcune giustificazioni per l'iniezione di dipendenza, ma la principale è per i test. Potresti avere un caso in cui hai un motore di persistenza di qualche tipo (come un database). Tuttavia, può essere fastidioso utilizzare un database reale quando si eseguono test ripetutamente. Quindi, per i tuoi casi di test, inseriresti un database fittizio, in modo da non incorrere in tale sovraccarico.
Utilizzando questo esempio, puoi vedere la differenza: abbiamo sempre intenzione di utilizzare una strategia di archiviazione dei dati, ed è quella che passiamo (l'istanza database reale). Tuttavia, nello sviluppo e nel test, vogliamo utilizzare dipendenze diverse, quindi iniettiamo concrezioni diverse.
Puoi utilizzare DI come modello di strategia, in modo da poter scambiare l'algoritmo necessario per ogni cliente, ma DI può andare oltre in quanto è un modo per disaccoppiare semplicemente le parti di un'applicazione, che non farebbe parte di il modello di strategia.
Sarebbe rischioso dire che DI è solo un modello di strategia rinominato in quanto inizia a diluire ciò per cui il modello di strategia è realmente, IMO.
Amico, l'iniezione di dipendenza è uno schema più generale, e riguarda la dipendenza dalle astrazioni non dalle concrezioni ed è una parte di ogni modello, ma il modello strategico è una soluzione a problemi più specifici
questa è la definizione da wikipedia:
DI:
L'iniezione di dipendenza (DI) nella programmazione di computer orientata agli oggetti è un modello di progettazione con un principio fondamentale di separazione del comportamento dalla risoluzione delle dipendenze. In altre parole: una tecnica per disaccoppiare componenti software altamente dipendenti.
Modello di strategia:
Nella programmazione di computer, il modello di strategia (noto anche come modello di politica) è un particolare modello di progettazione del software, in base al quale gli algoritmi possono essere selezionati in fase di esecuzione.
Il modello di strategia ha lo scopo di fornire un mezzo per definire una famiglia di algoritmi, incapsularli come un oggetto e renderli intercambiabili. Il modello di strategia consente agli algoritmi di variare indipendentemente dai client che li utilizzano.
Le strategie sono cose di livello superiore che vengono utilizzate per cambiare il modo in cui le cose vengono calcolate. Con l'inserimento delle dipendenze, puoi cambiare non solo il modo in cui le cose vengono calcolate, ma anche cambiare ciò che c'è.
Per me, diventa chiaro quando si usano gli unit test. Per l'esecuzione del codice di produzione, hai tutti i dati nascosti (cioè privati o protetti); considerando che, con gli unit test, la maggior parte dei dati è pubblica, quindi posso esaminarli con le asserzioni.
Esempio di strategia:
public class Cosine {
private CalcStrategy strat;
// Constructor - strategy passed in as a type of DI
public Cosine(CalcStrategy s) {
strat = s;
}
}
public abstract class CalcStrategy {
public double goFigure(double angle);
}
public class RadianStrategy extends CalcStrategy {
public double goFigure(double angle) {
return (...);
}
}
public class DegreeStrategy extends CalcStrategy {
public double goFigure(double angle) {
return (...);
}
}
Si noti che non esistono dati pubblici diversi tra le strategie. Né esistono metodi diversi. Entrambe le strategie condividono le stesse funzioni e firme.
Ora per l'inserimento delle dipendenze:
public class Cosine {
private Calc strat;
// Constructor - Dependency Injection.
public Cosine(Calc s) {
strat = s;
}
}
public class Calc {
private int numPasses = 0;
private double total = 0;
private double intermediate = 0;
public double goFigure(double angle) {
return(...);
}
public class CalcTestDouble extends Calc {
// NOTICE THE PUBLIC DATA.
public int numPasses = 0;
public double total = 0;
public double intermediate = 0;
public double goFigure(double angle) {
return (...);
}
}
Uso:
public CosineTest {
@Test
public void testGoFigure() {
// Setup
CalcTestDouble calc = new CalcTestDouble();
Cosine instance = new Cosine(calc);
// Exercise
double actualAnswer = instance.goFigure(0.0);
// Verify
double tolerance = ...;
double expectedAnswer = ...;
assertEquals("GoFigure didn't work!", expectedAnswer,
actualAnswer, tolerance);
int expectedNumPasses = ...;
assertEquals("GoFigure had wrong number passes!",
expectedNumPasses, calc.numPasses);
double expectedIntermediate = ...;
assertEquals("GoFigure had wrong intermediate values!",
expectedIntermediate, calc.intermediate, tolerance);
}
}
Notare gli ultimi 2 controlli. Hanno usato i dati pubblici nel test double che è stato iniettato nella classe sotto test. Non ho potuto farlo con il codice di produzione a causa del principio di nascondere i dati. Non volevo inserire un codice di test per scopi speciali nel codice di produzione. I dati pubblici dovevano essere in una classe diversa.
Il doppio di prova è stato iniettato. Ciò è diverso da una semplice strategia poiché ha influenzato i dati e non solo le funzioni.
L'iniezione di dipendenze è un perfezionamento del modello strategico che spiegherò brevemente. Spesso è necessario scegliere tra diversi moduli alternativi in fase di esecuzione. Questi moduli implementano tutti un'interfaccia comune in modo che possano essere utilizzati in modo intercambiabile. Lo scopo del pattern strategico è rimuovere l'onere di decidere su quale dei moduli usare (cioè quale "strategia concreta" o dipendenza) incapsulando il processo decisionale in un oggetto separato che chiamerò oggetto strategia.
L'inserimento delle dipendenze affina il modello di strategia non solo decidendo quale strategia concreta utilizzare, ma creando un'istanza della strategia concreta e "iniettandola" nuovamente nel modulo chiamante. Ciò è utile anche se esiste una sola dipendenza poiché la conoscenza di come gestire (inizializzare ecc.) L'istanza della strategia concreta può anche essere nascosta all'interno dell'oggetto strategia.
In realtà, anche l'inserimento delle dipendenze è molto simile al pattern Bridge. Per me (e secondo la definizione), il pattern Bridge è quello di accogliere diverse versioni dell'implementazione, mentre il pattern Strategy è per la logica totalmente diversa. Ma il codice di esempio sembra che utilizzi DI. Quindi forse DI è solo una tecnica o un'implementazione?
La strategia è un'arena per utilizzare le tue abilità di iniezione di dipendenza. I modi reali per implementare l'inserimento delle dipendenze sono i seguenti: -
C'è una cosa però che distingue la strategia. Come sai in Unity, quando l'applicazione si avvia, tutte le dipendenze sono impostate e non possiamo modificarle ulteriormente. Ma la strategia supporta la modifica della dipendenza dal runtime. Ma NOI dobbiamo gestire / iniettare la dipendenza, non la responsabilità della Strategia!
In realtà la strategia non parla di iniezione di dipendenza. Se necessario, può essere fatto tramite Abstract Factory all'interno di un pattern Strategy. La strategia parla solo di creare una famiglia di classi con interfaccia e di "giocarci". Durante il gioco, se troviamo che le classi sono in un livello diverso, dobbiamo iniettarlo noi stessi ma non il lavoro di Strategia.
Se consideriamo i principi SOLID - Usiamo il modello di strategia per il principio aperto chiuso e il principio di iniezione delle dipendenze per il principio di inversione delle dipendenze