Имеет ли этот шаблон стратегии Java избыточный класс контекста?

следующий пример кода является реализацией шаблона стратегии скопировал из Википедии. Мой полный вопрос следует за ним...

Вики 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 реализация с использованием существующих классов и интерфейс, кроме контекста и он будет работать точно так же. Он все еще слабо связан.

(( Edit: в этом простом сценарии, когда я выхожу из класса Context, буду ли я делать будущую ошибку? ))

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

}

Резюме Обновление

перечисление в форме точки, что было обнаружено с помощью ответов и комментариев:

  • контекст позволяет варьировать использование составной стратегии (например, время его вызова). Различные контексты могут выполнять различную внутреннюю работу до и после вызова данной стратегии.
  • контекст-это "черный ящик" высокого уровня. Контекстная логика может измениться, также может измениться составная стратегия (или другая используемая), не нарушая клиента, потому что клиент понимает только, как вызвать контекст.
  • несмотря на то, что я создал альтернативную реализацию образца кода Википедии, оставив контекст, и хотя он работал так же, как и оригинал, вся ситуация была упрощена (в обоих случаях), и мои изменения фактически означали: 1. это больше не стратегия, 2. Мне не хватает преимуществ духа стратегии, о которой здесь говорится.
  • моя альтернативная реализация использовала основной метод как контекст, поэтому я мог бы также сохранить контекст, если эффективно его имитировать. Создавая нечистую стратегическую модель, создавалась путаница. Мне не нужно было изобретать колесо или попытаться быть умнее (в данном случае).

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

4 ответов


как следует из названия,Context - это то, что инкапсулирует точку, в которой выполняется стратегия. Без этого у вас просто голый Strategy, и вызывающий класс теперь берет на себя дополнительную ответственность: зная, когда звонить . Ваш пример, возможно, слишком прост, и в этом конкретном случае я бы сказал, что 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 может выглядеть в этом случае:

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: опечатка в конструкторе фиксированных


Это может быть для этого выдуманного примера, но тогда я бы не назвал это ne plus ultra стратегии.

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

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


Context не будет избыточным в Strategy шаблон и он полезен в приведенных ниже сценариях:

  1. код для вызова конкретного Strategy распространяется в нескольких классах без вызова Context . В будущем, если вы должны повторно-фактор или изменить Strategy интерфейс, это будет лихорадочная задача.
  2. предположим, что некоторые данные должны быть заполнены перед вызовом определенной стратегии. Контекст лучше всего подходит здесь, предоставляя дополнительную информацию и назовем стратегический метод конкретной стратегии.

    например Context получит стратегию и userId в качестве параметра. Перед выполнением Strategy, Context нужно предоставить много дополнительной информации, связанной с профилем пользователя. Context будет получать необходимую информацию и выполняет стратегический метод стратегии. В отсутствие контекста, вы должны дублировать код в 100 разных местах, если вы вызываете стратегический метод в 100 разных местах.

  3. Context может принимать независимое решение о том, какую стратегию использовать. Он может просто изменить тип стратегии в зависимости от конфигурации времени выполнения. Стратегия core USP-это переключение между семейством связанных алгоритмов. Контекст-лучшее место для его достижения.

  4. Если вам нужно действовать по нескольким стратегиям,Context Это лучшее место. axtavt предлагаемый ответ, используя Accumulator один образец.

см. этот пост более подробно.

реальный пример шаблона стратегии