Jaka jest różnica między wzorcem strategii a iniekcją zależności?

96

Wzorzec strategii i wstrzykiwanie zależności pozwalają nam ustawiać / wstrzykiwać obiekty w czasie wykonywania. Jaka jest różnica między wzorcem strategii a iniekcją zależności?

Nero
źródło
Wzorzec strategii może wykorzystywać Dependency Injection
TechWisdom

Odpowiedzi:

110

DI i strategia działają w ten sam sposób, ale strategia jest używana dla bardziej drobnoziarnistych i krótkotrwałych zależności.

Gdy obiekt jest skonfigurowany za pomocą „ustalonej” strategii, na przykład podczas konstruowania obiektu, rozróżnienie między strategią a DI zaciera się. Ale w scenariuszu DI bardziej niezwykłe jest to, że zależności obiektów zmieniają się podczas ich życia, podczas gdy nie jest to rzadkie w przypadku strategii.

Ponadto można przekazywać strategie jako argumenty do metod, podczas gdy powiązana koncepcja wstrzykiwania argumentów metody nie jest rozpowszechniona i jest używana głównie w kontekście testów automatycznych.

Strategia koncentruje się na intencji i zachęca do tworzenia interfejsu z różnymi implementacjami, które są zgodne z tym samym kontraktem behawioralnym. DI polega bardziej na po prostu zaimplementowaniu jakiegoś zachowania i zapewnieniu go.

Dzięki DI możesz rozłożyć swój program z innych powodów niż tylko po to, aby móc zamienić części implementacji. Interfejs używany w DI z tylko jedną implementacją jest bardzo powszechny. „Strategia” z tylko jednym konkretnym wdrożeniem (kiedykolwiek) nie jest prawdziwym problemem, ale prawdopodobnie jest bliższa DI.

eljenso
źródło
Interfejs używany w DI z tylko jedną implementacją jest bardzo powszechny - więc czym jest DI w tym konkretnym przypadku?
Kalpesh Soni
3
Ten cytat w zasadzie wyjaśnia wszystko:in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
Siergiej Telshevsky
Strategia: klasy są zaprojektowane tak, aby można je było skonfigurować za pomocą algorytmu w czasie wykonywania. DI: Takie klasy otrzymują algorytm (obiekt strategii) wprowadzany w czasie wykonywania. Z pamięci wzorców projektowych GoF na w3sdesign.com .
GFranke
39

Różnica polega na tym, co próbują osiągnąć. Wzorzec strategii jest używany w sytuacjach, w których wiesz, że chcesz zamienić implementacje. Na przykład możesz chcieć sformatować dane na różne sposoby - możesz użyć wzorca strategii, aby zamienić program formatujący XML lub program formatujący CSV itp.

Dependency Injection różni się tym, że użytkownik nie próbuje zmienić zachowania w czasie wykonywania. Zgodnie z powyższym przykładem możemy tworzyć program eksportujący XML, który używa programu formatującego XML. Zamiast konstruować kod w ten sposób:

public class DataExporter() {
  XMLFormatter formatter = new XMLFormatter();
}

można 'wstrzyknąć' program formatujący do konstruktora:

public class DataExporter {
  IFormatter formatter = null;

  public DataExporter(IDataFormatter dataFormatter) {
    this.formatter = dataFormatter;
  }
}

DataExporter exporter = new DataExporter(new XMLFormatter());

Istnieje kilka uzasadnień dla Dependency Injection, ale podstawowe dotyczy testowania. Może się zdarzyć, że masz jakiś silnik trwałości (na przykład bazę danych). Jednak korzystanie z prawdziwej bazy danych może być uciążliwe, gdy wielokrotnie przeprowadzasz testy. Tak więc w przypadku przypadków testowych wstrzyknąłbyś fikcyjną bazę danych, aby nie ponosić tego narzutu.

Korzystając z tego przykładu, możesz zobaczyć różnicę: zawsze planujemy używać strategii przechowywania danych i to ta, którą przekazujemy (prawdziwa instancja bazy danych). Jednak w programowaniu i testowaniu chcemy używać różnych zależności, więc wstrzykujemy różne konkrecje.

tsimon
źródło
28

Możesz użyć DI jako wzorca strategii, więc możesz zamienić algorytm, który jest potrzebny dla każdego klienta, ale DI może wykraczać poza to, ponieważ jest to sposób na oddzielenie części aplikacji, które nie byłyby częścią wzorzec strategii.

Ryzykowne byłoby stwierdzenie, że DI to tylko wzorzec strategii o zmienionej nazwie, ponieważ zaczyna on osłabiać to, do czego naprawdę służy wzorzec strategii, IMO.

James Black
źródło
2
Myślę, że rozumiem twoją istotę, ale nie mogę ująć tego poprawnie w słowach ... Więc twoje powiedzenie DI jest bardziej wzorcem wdrażania, podczas gdy strategia jest bardziej wzorcem projektowym, a jednym ze sposobów wdrażania strategii jest DI?
Robert Gould
1
To brzmi jak dobry sposób na określenie tego. DI to coś więcej niż wzorzec strategii. Znalazłem to samo zamieszanie z AOP, gdzie ludzie myślą, że jest to wzór fabryczny. Myślę, że DI może wdrożyć wzorzec strategii, więc twoje przeredagowanie wydawałoby się fantastyczne. :)
James Black
15

Stary, wstrzykiwanie zależności to bardziej ogólny wzorzec i chodzi o zależność od abstrakcji, a nie konkrecji i jest częścią każdego wzorca, ale wzorzec strategii jest rozwiązaniem bardziej szczegółowego problemu

to jest definicja z wikipedii:

DI:

Wstrzykiwanie zależności (DI) w programowaniu komputerowym zorientowanym obiektowo to wzorzec projektowy, którego podstawową zasadą jest oddzielenie zachowania od rozwiązywania zależności. Innymi słowy: technika oddzielania wysoce zależnych komponentów oprogramowania.

Wzorzec strategii:

W programowaniu komputerowym wzorzec strategii (znany również jako wzorzec polityki) to określony wzorzec projektowy oprogramowania, w którym algorytmy można wybierać w czasie wykonywania.

Wzorzec strategii ma na celu zapewnienie środków do zdefiniowania rodziny algorytmów, hermetyzacji każdego z nich jako obiektu i uczynienia ich wymiennymi. Wzorzec strategii umożliwia różnym algorytmom niezależnie od klientów, którzy ich używają.

Jahan
źródło
3
Szczególnie podoba mi się część „koleś” w twoim wyjaśnieniu. :-)
johey
7

Strategie to rzeczy wyższego poziomu, które służą do zmiany sposobu obliczania rzeczy. Dzięki iniekcji zależności możesz zmienić nie tylko sposób obliczania rzeczy, ale także zmienić to, co jest.

Dla mnie staje się to jasne, gdy używam testów jednostkowych. W celu wykonania kodu produkcyjnego wszystkie dane są ukryte (tj. Prywatne lub chronione); mając na uwadze, że w przypadku testów jednostkowych większość danych jest publiczna, więc mogę spojrzeć na nie za pomocą Asserts.


Przykład strategii:

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 (...);
  }
}

Zauważ, że nie ma danych publicznych, które różniłyby się między strategiami. Nie ma też innych metod. Obie strategie mają te same funkcje i podpisy.


Teraz do wstrzyknięcia zależności:

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 (...);
  }
}

Posługiwać się:

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);
  }
}

Zwróć uwagę na ostatnie 2 kontrole. Wykorzystali publiczne dane z podwójnego testu, które zostały wstrzyknięte do testowanej klasy. Nie mogłem tego zrobić z kodem produkcyjnym ze względu na zasadę ukrywania danych. Nie chciałem, aby kod do testów specjalnych został wstawiony do kodu produkcyjnego. Dane publiczne musiały znajdować się w innej klasie.

Testowy podwójny został wstrzyknięty. To coś innego niż tylko strategia, ponieważ wpłynęło to na dane, a nie tylko na funkcje.

Edward Ames
źródło
4

Wstrzyknięcie zależności jest udoskonaleniem wzorca strategii, który pokrótce wyjaśnię. W czasie wykonywania często konieczne jest wybranie jednego z kilku alternatywnych modułów. Wszystkie te moduły implementują wspólny interfejs, dzięki czemu mogą być używane zamiennie. Celem wzorca strategii jest usunięcie ciężaru decydowania o tym, którego z modułów użyć (tj. Jakiej „konkretnej strategii” lub zależności) poprzez zamknięcie procesu decyzyjnego w oddzielnym obiekcie, który będę nazywać obiektem strategii.

Wstrzykiwanie zależności udoskonala wzorzec strategii, nie tylko decydując, której konkretnej strategii użyć, ale także tworząc instancję konkretnej strategii i „wstrzykując” ją z powrotem do modułu wywołującego. Jest to przydatne, nawet jeśli istnieje tylko jedna zależność, ponieważ wiedza o tym, jak zarządzać (inicjalizować itp.) Konkretną instancją strategii, może być również ukryta w obiekcie strategii.

Andrew W. Phillips
źródło
1

W rzeczywistości iniekcja zależności również wygląda bardzo podobnie do wzorca Bridge. Dla mnie (i zgodnie z definicją) wzorzec Bridge ma uwzględniać różne wersje implementacji, podczas gdy wzorzec Strategii jest dla zupełnie innej logiki. Ale przykładowy kod wygląda na to, że używa DI. Więc może DI to tylko technika lub implementacja?

Calvin
źródło
0

Strategia jest areną do wykorzystania umiejętności wstrzykiwania zależności. Prawdziwe sposoby implementacji iniekcji zależności są następujące: -

  1. Wydarzenia
  2. Pliki konfiguracyjne mapy jedności / struktury (lub programowo) itp.
  3. Metody rozszerzeń
  4. Abstrakcyjny wzór fabryki
  5. Odwrócenie wzorca sterowania (używane zarówno przez strategię, jak i przez fabrykę abstrakcyjną)

Jest jednak jedna rzecz, która wyróżnia strategię. Jak wiesz, w Unity podczas uruchamiania aplikacji wszystkie zależności są ustawione i nie możemy tego dalej zmieniać. Ale strategia obsługuje zmianę zależności środowiska wykonawczego. Ale my musimy zarządzać / wstrzykiwać zależność, a nie odpowiedzialność Strategii!

W rzeczywistości strategia nie mówi o wstrzykiwaniu zależności. W razie potrzeby można to zrobić za pośrednictwem Abstract Factory w ramach wzorca strategii. Strategia mówi tylko o stworzeniu rodziny klas z interfejsem i „zabawie” z nim. Podczas gry, jeśli stwierdzimy, że klasy są na innym poziomie, musimy sami to wstrzyknąć, ale nie musimy to robić jako strategia.

Niebieskie chmury
źródło
0

Jeśli weźmiemy pod uwagę zasady SOLID - używamy wzorca strategii dla zasady otwartej zamkniętej i iniekcji zależności dla zasady odwrócenia zależności

Sumeet Patil
źródło
1
Nie jestem pewien, czy rozumiem, czy mógłbyś wyjaśnić, w jaki sposób strategia odnosi się do zasady Open / Closed i jak DI odnosi się do DIP?
Adam Parkin