Вопрос по design-patterns, strategy-pattern, java – Имеет ли этот шаблон стратегии Java избыточный класс Context?

14

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

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

((Редактировать: В этом простом сценарии, когда я пропускаю класс 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. Я скучаю по преимуществам духа шаблона стратегии, которые упомянуты здесь.Моя альтернативная реализация использовала основной метод, такой как Context, поэтому я мог бы также сохранить Context, если эффективно имитирую его. Создав шаблон нечистой Стратегии, возникла путаница. Я не'Не нужно изобретать велосипед или пытаться быть умнее (в этом случае).

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

Ваш Ответ

4   ответа
3

но тогда я бы неЭто не плюс стратегия Ultra.

Класс Context демонстрирует, как вы можете дать классу другое поведение, просто передав новую конкретную реализацию интерфейса. Так как класс знает только интерфейс, ничего не должно измениться. Тот'Суть в том. Дон»Третий пример слишком буквально.

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

I added a Summary Update section beneath the original question to consolidate findings. John K
Я понимаю, что вы имеете ввиду. Я фактически заменил основной метод для класса Context, в связи с чем возникает вопрос, почему бы просто не использовать класс Context, как это определено в шаблоне Strategy, чтобы абстрагироваться от использования стратегии, как вы описали. Это полезно для меня, потому что я могу думать об этом с точки зрения того, что я хотел бы поместить в основной метод "упростить» вещи (например, сделать их немного неправильно) и понять, чтоЭто то, что я могу поместить в контекст. Благодарю. John K
4

Это лучший пример того, как настоящий "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;
    } 
}
</integer>

РЕДАКТИРОВАТЬ: Исправлена опечатка в конструкторе

Похоже, в конструкторе есть опечатка. Paul Morie
Правильно ли я вижу, имя класса "Накопитель» и имя конструктораКонтекст»? Я родом из мира C #, так что, возможно, яЯ здесь что-то ошибаюсь и упускаю из виду. John K
16

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

Пример, который, возможно, лучше иллюстрирует полезность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.
  }
}
</shippingitem>

Обратите внимание, какStrategy никогда не будет вызван напрямую с внешнего клиента. ТолькоshipItems использует стратегию, а подробности следующих шагов - черный ящик. Это позволяетContext настроить, как он использует стратегию, не влияя на клиентов. Например, шаги могут быть полностью переупорядочены или скорректированы (или полностью удалены) для достижения целей производительности или других целей - но для клиента внешний интерфейсshipItems() выглядит точно так же.

Также обратите внимание, что наш примерContext,LoadingDock, мог изменить ееLoadStrategy в любое время в зависимости от его внутреннего состояния. Например, если док становится слишком полным, возможно, он переключится на более агрессивный механизм планирования, который быстрее доставляет ящики с дока в грузовики, жертвуя при этом некоторой эффективностью (возможно, грузовики этого не делают.загрузить так эффективно, как они могли бы).

@ Джон Феминелла: ЭтоСправедливо и способствует меньшей путанице, чем использование полиморфного поведения.Also I added a Summary Update section beneath the original question to consolidate findings. John K
@RaduMurzea You 'Добро пожаловать! Рад, что это было полезно. John Feminella
Это подробное объяснение, в котором четко обозначена важность контекста, включая пример кода поддержки. Отлично. John K
Хм. Я'я не уверен, что мне нравится идея полиморфногоContext, ИМО, это 'было бы лучше объединить разные части в единыйContext, а затем иметь соответствующие стратегии для каждого, в зависимости от обстоятельств. John Feminella
0

Context победил'быть лишним вStrategy шаблон и это полезно в следующих сценариях:

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

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

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

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

Если вам нужно действовать по нескольким стратегиям,Context это лучшее место. Axtavt предложил ответ использованияAccumulator это один пример.

Обратитесь к этому сообщению более подробно.

Пример шаблона стратегии в реальном мире

Похожие вопросы