В чем разница между шаблоном стратегии и внедрением зависимостей?

96

И шаблон стратегии, и внедрение зависимостей позволяют нам устанавливать / внедрять объекты во время выполнения. В чем разница между шаблоном стратегии и внедрением зависимостей?

Неро
источник
Шаблон стратегии может использовать
внедрение

Ответы:

110

DI и стратегия работают одинаково, но стратегия используется для более тонких и кратковременных зависимостей.

Когда объект сконфигурирован с «фиксированной» стратегией, например, когда объект построен, различие между стратегией и DI размывается. Но в сценарии DI более необычно, что зависимости объектов меняются в течение их жизни, в то время как это не редкость для стратегии.

Кроме того, вы можете передавать стратегии в качестве аргументов методам, в то время как соответствующая концепция внедрения аргументов метода не получила широкого распространения и в основном используется только в контексте автоматизированного тестирования.

Стратегия фокусируется на намерении и побуждает вас создавать интерфейс с различными реализациями, которые подчиняются одному и тому же поведенческому контракту. DI - это больше просто реализация некоторого поведения и обеспечение его.

С помощью DI вы можете декомпозировать свою программу не только для того, чтобы иметь возможность менять местами части реализации. Интерфейс, используемый в DI только с одной реализацией, очень распространен. «Стратегия» только с одной конкретной реализацией (когда-либо) не является реальной проблемой, но, вероятно, ближе к DI.

Эльдженсо
источник
Интерфейс, используемый в DI только с одной реализацией, очень распространен - ​​так что же тогда такое DI в данном конкретном случае?
Kalpesh Soni
3
Эта цитата в основном все объясняет:in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
Сергей Тельшевский
Стратегия: классы разработаны таким образом, чтобы их можно было настроить с помощью алгоритма во время выполнения. DI: Такие классы получают алгоритм (объект стратегии), внедряемый во время выполнения. Из памяти шаблонов проектирования GoF на сайте w3sdesign.com .
GFranke 05
39

Разница в том, чего они пытаются достичь. Шаблон стратегии используется в ситуациях, когда вы знаете, что хотите поменять реализации. Например, вы можете захотеть отформатировать данные по-разному - вы можете использовать шаблон стратегии для замены модуля форматирования XML или модуля форматирования CSV и т. Д.

Внедрение зависимостей отличается тем, что пользователь не пытается изменить поведение во время выполнения. Следуя приведенному выше примеру, мы могли бы создать программу экспорта XML, которая использует средство форматирования XML. Вместо того, чтобы структурировать код следующим образом:

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

вы бы 'вставили' форматтер в конструктор:

public class DataExporter {
  IFormatter formatter = null;

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

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

Есть несколько оправданий для внедрения зависимостей, но главное - для тестирования. У вас может быть случай, когда у вас есть какой-то механизм сохранения (например, база данных). Однако использование реальной базы данных при многократном выполнении тестов может быть проблемой. Итак, для ваших тестовых случаев вы должны внедрить фиктивную базу данных, чтобы избежать этих накладных расходов.

Используя этот пример, вы можете увидеть разницу: мы всегда планируем использовать стратегию хранения данных, и это та, которую мы передаем (реальный экземпляр БД). Однако при разработке и тестировании мы хотим использовать разные зависимости, поэтому вводим разные конкреции.

Цимон
источник
28

Вы можете использовать DI в качестве шаблона стратегии, чтобы вы могли поменять местами алгоритм, необходимый для каждого клиента, но DI может выйти за рамки этого, поскольку это способ просто разделить части приложения, которые не были бы частью шаблон стратегии.

Было бы рискованно говорить, что DI - это просто переименованный шаблон стратегии, поскольку это начинает размывать то, для чего на самом деле этот шаблон стратегии, ИМО.

Джеймс Блэк
источник
2
Я думаю, что понимаю вашу суть, но я не могу правильно выразить это словами ... Итак, вы говорите, что DI - это скорее шаблон реализации, в то время как стратегия - это скорее шаблон дизайна, и один из способов реализации стратегии - через DI?
Роберт Гулд
1
Звучит неплохо. DI - это больше, чем просто шаблон стратегии. Я обнаружил ту же путаницу с АОП, когда люди думают, что это заводской шаблон. Я думаю, что DI может реализовать шаблон стратегии, так что ваша переформулировка будет фантастической. :)
Джеймс Блэк
15

Чувак, внедрение зависимостей - это более общий шаблон, и он о зависимости от абстракций, а не от конкреций, и это часть каждого шаблона, но шаблон стратегии - это решение более конкретной проблемы

это определение из Википедии:

DI:

Внедрение зависимостей (DI) в объектно-ориентированном программировании - это шаблон проектирования с основным принципом отделения поведения от разрешения зависимостей. Другими словами: метод разделения сильно зависимых программных компонентов.

Шаблон стратегии:

В компьютерном программировании шаблон стратегии (также известный как шаблон политики) представляет собой особый шаблон проектирования программного обеспечения, посредством которого алгоритмы могут выбираться во время выполнения.

Шаблон стратегии предназначен для того, чтобы предоставить средства для определения семейства алгоритмов, инкапсуляции каждого из них как объекта и обеспечения их взаимозаменяемости. Шаблон стратегии позволяет алгоритмам варьироваться независимо от клиентов, которые их используют.

Джахан
источник
3
Мне особенно нравится "чувак" в вашем объяснении. :-)
johey
7

Стратегии - это вещи более высокого уровня, которые используются для изменения способа вычислений. С помощью внедрения зависимостей вы можете изменить не только способ вычисления, но и то, что там есть.

Для меня это становится понятно при использовании юнит-тестов. Для выполнения производственного кода все данные скрыты (т.е. закрыты или защищены); тогда как в модульных тестах большая часть данных является общедоступной, поэтому я могу просматривать их с помощью Asserts.


Пример стратегии:

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

Обратите внимание, что нет общедоступных данных, различающихся между стратегиями. Нет и других методов. Обе стратегии имеют одинаковые функции и подписи.


Теперь о внедрении зависимости:

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

Использование:

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

Обратите внимание на последние 2 проверки. Они использовали общедоступные данные в тестовом двойнике, который был введен в тестируемый класс. Я не мог этого сделать с производственным кодом из-за принципа сокрытия данных. Я не хотел, чтобы код специального назначения был вставлен в производственный код. Публичные данные должны относиться к другому классу.

Был введен тестовый дублер. Это отличается от стратегии, поскольку она затрагивает данные, а не только функции.

Эдвард Эймс
источник
4

Внедрение зависимостей - это уточнение шаблона стратегии, который я кратко объясню. Часто во время выполнения необходимо выбрать один из нескольких альтернативных модулей. Все эти модули реализуют общий интерфейс, поэтому их можно использовать взаимозаменяемо. Целью шаблона стратегии является снятие бремени принятия решения о том, какой из модулей использовать (т.е. какую «конкретную стратегию» или зависимость), путем инкапсуляции процесса принятия решения в отдельный объект, который я буду называть объектом стратегии.

Внедрение зависимостей уточняет шаблон стратегии, не только решая, какую конкретную стратегию использовать, но и создавая экземпляр конкретной стратегии и «вводя» его обратно в вызывающий модуль. Это полезно, даже если есть только одна зависимость, поскольку знание того, как управлять (инициализировать и т. Д.) Конкретным экземпляром стратегии, также может быть скрыто внутри объекта стратегии.

Эндрю В. Филлипс
источник
1

На самом деле внедрение зависимостей также очень похоже на паттерн Мост. Для меня (и согласно определению) шаблон «Мост» предназначен для разных версий реализации, а шаблон «Стратегия» - для совершенно другой логики. Но пример кода выглядит так, как будто он использует DI. Так, может быть, DI - это просто техника или реализация?

Кальвин
источник
0

Стратегия - это арена для использования ваших навыков внедрения зависимостей. Реальные способы реализации внедрения зависимостей следующие:

  1. События
  2. Файлы конфигурации карты единства / структуры (или программно) и т. Д.
  3. Методы расширения
  4. Абстрактный узор фабрики
  5. Инверсия шаблона управления (используется как стратегией, так и абстрактной фабрикой)

Однако есть одна вещь, которая отличает стратегию от других. Как вы знаете, в Unity при запуске приложения все зависимости устанавливаются, и мы не можем изменить их дальше. Но стратегия поддерживает изменение зависимостей во время выполнения. Но МЫ должны управлять / внедрять зависимость, а не ответственность Стратегии!

На самом деле стратегия не говорит о внедрении зависимостей. При необходимости это можно сделать с помощью абстрактной фабрики внутри шаблона стратегии. Стратегия говорит только о создании семейства классов с интерфейсом и «игре» с ним. Если во время игры мы обнаружим, что классы находятся на другом уровне, мы должны внедрить его сами, но это не задача Стратегии.

Синие облака
источник
0

Если мы рассмотрим принципы SOLID - мы используем шаблон стратегии для принципа открытого и закрытого состояния и внедрение зависимостей для принципа инверсии зависимостей.

Сумит Патил
источник
1
Не уверен, что понимаю, не могли бы вы подробнее рассказать о том, как стратегия соотносится с принципом открытия / закрытия и как DI соотносится с DIP?
Адам Паркин