И шаблон стратегии, и внедрение зависимостей позволяют нам устанавливать / вводить объекты во время выполнения. В чем разница между шаблоном стратегии и внедрением зависимостей?
В чем разница между шаблоном стратегии и внедрением зависимостей?
Ответы (9)
DI и стратегия работают одинаково, но стратегия используется для более тонких и краткосрочных зависимостей.
Когда объект сконфигурирован с «фиксированной» стратегией, например, когда объект построен, различие между стратегией и DI размывается. Но в сценарии DI более необычно, что зависимости объектов меняются в течение их жизненного цикла, в то время как это не редкость для стратегии.
Кроме того, вы можете передавать стратегии в качестве аргументов методам, в то время как соответствующая концепция внедрения аргументов метода не получила широкого распространения и в основном используется только в контексте автоматического тестирования.
Стратегия фокусируется на намерении и побуждает вас создавать интерфейс с различными реализациями, которые подчиняются одному и тому же поведенческому контракту. DI - это больше просто реализация некоторого поведения и обеспечение его.
С помощью DI вы можете декомпозировать свою программу по другим причинам, а не просто для того, чтобы иметь возможность поменять местами части реализации. Интерфейс, используемый в DI только с одной реализацией, очень распространен. «Стратегия» только с одной конкретной реализацией (когда-либо) не является реальной проблемой, но, вероятно, ближе к 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
- person Sergey Telshevsky; 22.05.2014
Разница в том, чего они пытаются достичь. Шаблон стратегии используется в ситуациях, когда вы знаете, что хотите поменять реализации. Например, вы можете захотеть отформатировать данные по-разному - вы можете использовать шаблон стратегии для замены модуля форматирования 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());
Есть несколько оправданий для внедрения зависимостей, но главное - для тестирования. У вас может быть случай, когда у вас есть какой-то механизм сохранения (например, база данных). Однако при многократном выполнении тестов использование реальной базы данных может быть затруднительным. Итак, для ваших тестовых случаев вы должны внедрить фиктивную базу данных, чтобы избежать этих накладных расходов.
Используя этот пример, вы можете увидеть разницу: мы всегда планируем использовать стратегию хранения данных, и это та, которую мы передаем (реальный экземпляр БД). Однако при разработке и тестировании мы хотим использовать разные зависимости, поэтому вводим разные конкреции.
Вы можете использовать DI в качестве шаблона стратегии, чтобы вы могли поменять местами алгоритм, необходимый для каждого клиента, но DI может выйти за рамки этого, поскольку это способ просто разделить части приложения, которые не были бы частью шаблон стратегии.
Было бы рискованно говорить, что DI - это просто переименованный шаблон стратегии, поскольку это начинает размывать то, для чего на самом деле шаблон стратегии, ИМО.
Чувак, внедрение зависимости - это более общий шаблон, и он о зависимости от абстракций, а не от конкреций, и это часть каждого шаблона, но шаблон стратегии - это решение более конкретной проблемы
это определение из Википедии:
DI:
Внедрение зависимостей (DI) в объектно-ориентированном компьютерном программировании - это шаблон проектирования с основным принципом отделения поведения от разрешения зависимостей. Другими словами: метод разделения сильно зависимых программных компонентов.
Шаблон стратегии:
В компьютерном программировании шаблон стратегии (также известный как шаблон политики) представляет собой особый шаблон проектирования программного обеспечения, посредством которого алгоритмы могут выбираться во время выполнения.
Шаблон стратегии предназначен для предоставления средств для определения семейства алгоритмов, инкапсуляции каждого из них как объекта и обеспечения их взаимозаменяемости. Шаблон стратегии позволяет алгоритмам варьироваться независимо от клиентов, которые их используют.
Стратегии - это вещи более высокого уровня, которые используются для изменения способа вычислений. С помощью внедрения зависимостей вы можете изменить не только способ вычисления, но и то, что там есть.
Для меня это становится понятно при использовании юнит-тестов. Для выполнения производственного кода все данные скрыты (т. Е. Закрыты или защищены); тогда как в модульных тестах большая часть данных является общедоступной, поэтому я могу просматривать их с помощью 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 проверки. Они использовали общедоступные данные в тестовом двойнике, который был введен в тестируемый класс. Я не мог этого сделать с производственным кодом из-за принципа сокрытия данных. Я не хотел, чтобы код специального назначения был вставлен в производственный код. Публичные данные должны принадлежать к другому классу.
Был введен тестовый дублер. Это отличается от стратегии, поскольку влияет на данные, а не только на функции.
Внедрение зависимостей - это уточнение шаблона стратегии, который я кратко объясню. Часто во время выполнения необходимо выбирать между несколькими альтернативными модулями. Все эти модули реализуют общий интерфейс, поэтому их можно использовать взаимозаменяемо. Целью шаблона стратегии является снятие бремени принятия решения о том, какой из модулей использовать (т.е. какую «конкретную стратегию» или зависимость), путем инкапсуляции процесса принятия решения в отдельный объект, который я буду называть объектом стратегии.
Внедрение зависимостей уточняет шаблон стратегии, не только решая, какую конкретную стратегию использовать, но и создавая экземпляр конкретной стратегии и «вводя» его обратно в вызывающий модуль. Это полезно, даже если существует только одна зависимость, поскольку знание того, как управлять (инициализировать и т. Д.) Конкретным экземпляром стратегии, также может быть скрыто внутри объекта стратегии.
На самом деле внедрение зависимостей также очень похоже на шаблон «Мост». Для меня (и согласно определению) шаблон «Мост» предназначен для различных версий реализации, а шаблон «Стратегия» предназначен для совершенно другой логики. Но пример кода выглядит так, как будто он использует DI. Так, может быть, DI - это просто техника или реализация?
Стратегия - это арена для использования ваших навыков внедрения зависимостей. Реальные способы реализации внедрения зависимостей следующие:
- События
- Файлы конфигурации карты единства / структуры (или программно) и т. Д.
- Методы расширения
- Абстрактный узор фабрики
- Инверсия шаблона управления (используется как стратегией, так и абстрактной фабрикой)
Однако есть одна вещь, которая отличает стратегию от других. Как вы знаете, в Unity при запуске приложения все зависимости устанавливаются, и мы не можем изменить их дальше. Но стратегия поддерживает изменение зависимостей во время выполнения. Но МЫ должны управлять / внедрять зависимость, а не ответственность Стратегии!
На самом деле стратегия не говорит о внедрении зависимостей. При необходимости это можно сделать с помощью абстрактной фабрики внутри шаблона стратегии. Стратегия говорит только о создании семейства классов с интерфейсом и «игре» с ним. Если во время игры мы обнаружим, что классы находятся на другом уровне, мы должны внедрить его сами, но это не задача Стратегии.
Если мы рассмотрим принципы SOLID - мы используем шаблон стратегии для принципа открытого закрытия и внедрение зависимостей для принципа инверсии зависимостей.