Следующий пример кода является реализацией Стратегической модели, скопированной с Википедии. Мой полный вопрос следует за ним...
Wiki main
метод:
//StrategyExample test application
class StrategyExample {
public static void main(String[] args) {
Context context;
// Three contexts following different strategies
context = new Context(new ConcreteStrategyAdd());
int resultA = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategySubtract());
int resultB = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategyMultiply());
int resultC = context.executeStrategy(3,4);
}
}
Части шаблона:
// The classes that implement a concrete strategy should implement this
// The context class uses this to call the concrete strategy
interface Strategy {
int execute(int a, int b);
}
// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyA's execute()");
return a + b; // Do an addition with a and b
}
}
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyB's execute()");
return a - b; // Do a subtraction with a and b
}
}
class ConcreteStrategyMultiply implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyC's execute()");
return a * b; // Do a multiplication with a and b
}
}
// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {
private Strategy strategy;
// Constructor
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
При рассмотрении конкретно вышеупомянутого примера, Context
избыточный класс?
Например, я могу придумать следующую альтернативу main
реализация при помощи существующих классов и интерфейса кроме Контекста и это будет работать точно то же. Это все еще слабо связывается.
((Редактирование: В этом простом сценарии, когда я не учту класс Контекста, я буду делать будущую ошибку?))
public static void main(String[] args) {
IStrategy strategy;
// Three strategies
strategy = new ConcreteStrategyAdd();
int resultA = strategy.executeStrategy(3,4);
strategy = new ConcreteStrategySubtract();
int resultB = strategy.executeStrategy(3,4);
strategy = new ConcreteStrategyMultiply();
int resultC = strategy.executeStrategy(3,4);
}
Список в точке формирует то, что было обнаружено через ответы и комментарии:
Если какие-либо другие точки были бы полезны или если этому нужен отпуск исправления комментарий, и я изменю список соответственно.
Как следует из названия, Context
- это то, что инкапсулирует точку, в которой осуществляется стратегия. Без этого у вас просто голая стратегия
, и вызывающий класс теперь берет на себя дополнительную ответственность: знать, когда вызывать саму стратегию
. Ваш пример, пожалуй, слишком прост, и в данном конкретном случае, Я бы сказал, что Context
не слишком многого добивается.
Пример, который, возможно, лучше иллюстрирует полезность Context
, больше похож на следующий:
public class LoadingDock { // Context.
private LoadStrategy ls; // Strategy.
public void setLoadStrategy(LoadStrategy ls) { ... }
// Clients of LoadingDock use this method to do the relevant work, rather
// than taking the responsibility of invoking the Strategy themselves.
public void shipItems(List<ShippingItem> l) {
// verify each item is properly packaged \
// ... | This code is complex and shouldn't be
// verify all addresses are correct | subsumed into consumers of LoadingDock.
// ... | Using a Context here is a win because
// load containers onto available vehicle | now clients don't need to know how a
Vehicle v = VehiclePool.fetch(); // | LoadingDock works or when to use a
ls.load(v, l); // / LoadStrategy.
}
}
Заметьте, как Strategy
никогда не будет вызываться напрямую от внешнего клиента. Только shipItems
использует эту стратегию, а детали последующих шагов - это черный ящик. Это позволяет Context
настроить, как она использует стратегию, не затрагивая клиентов. Например, шаги могут быть полностью переупорядочены или скорректированы (или полностью удалены) для достижения целей производительности или других целей -- но для клиента внешний интерфейс shipItems()
выглядит точно так же.
Заметьте также, что наш пример Context
, LoadingDock
, может изменить свою LoadStrategy
в любое время, основываясь на своем внутреннем состоянии. Например, если док переполняется, возможно, он переключится на более агрессивный механизм планирования, который будет быстрее доставлять ящики из дока в грузовики, жертвуя при этом некоторой эффективностью (возможно, грузовики загружаются не так эффективно, как могли бы)
Это лучший пример того, как реальный "Context
" класс может выглядеть в этом сценарии:
class Accumulator {
private Strategy strategy;
public Accumulator(Strategy strategy) {
this.strategy = strategy;
}
public int accumulate(List<Integer> values) {
int result = values.get(0);
for (int i = 1; i < values.size(); i++) {
result = strategy.execute(result, values.get(i));
}
return result;
}
}
EDIT: Typo в конструкторе fix
.Это может быть и для вымышленного примера, но тогда я бы не стал называть это ne plus ultra из Strategy.
Контекстный класс демонстрирует, как можно дать классу другое поведение, просто передав новую конкретную реализацию интерфейса. Так как класс знает только интерфейс, ничего менять не надо. В том-то и дело. Не берите слишком буквально остальную часть примера.
То, как вы это закодировали, сработает, но суть в том, что вы выкинули это в основной метод. Это будет не тот способ, которым вы обычно используете Strategy. Вы будете делать это внутри класса, и Context является простым примером этого.