Подтвердить что ты не робот

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

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

Метод 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 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 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 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);

}

Сводное обновление

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

РЕДАКТИРОВАТЬ: Typo в фиксированном конструкторе

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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

Подробнее см. в этом сообщении.

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