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

Когда и почему должен использоваться шаблон стратегии?

Когда будет использоваться шаблон стратегии?

Я вижу фрагменты кода клиента следующим образом:


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

    }

}

и похоже, что вы можете просто реорганизовать его на это:


class StrategyExample {

    public static void main(String[] args) {
         // Three contexts following different strategies
        int resultA =new ConcreteStrategyAdd().execute(3,4);
        int resultB =new ConcreteStrategySubtract().execute(3,4);
        int resultC =new ConcreteStrategyMultiply().execute(3,4);
    }

}

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

4b9b3361

Ответ 1

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

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

interface GraphStrategy {
    Image renderGraph(Data graphData);
}

class BigGraphStratedy implements GraphStrategy {
    ...
}

class SmallGraphStrategy implements GraphStrategy {
    ...
}

Затем в другом коде:

GraphStrategy graphStrategy;

if (phoneBrowser == true) { 
    graphStrategy = new SmallGraphStrategy();
} else {
    graphStrategy = new BigGraphStrategy();
}

Остальная часть кода приложения может просто использовать graphStrategy.renderGraph(), не зная, выполняется ли полная или небольшая рендеринг изображений.

Ответ 2

Области, которые приходят на ум:

  • Распределитель ресурсов. В ручном управлении ресурсами это может быть сведено к минимуму времени, которое требуется для выделения ресурса, или минимизации фрагментации. Каждая стратегия здесь имеет метод "Allocate", который имеет один и тот же интерфейс, при этом пользователь принимает решение о том, какую стратегию использовать на основе того, что они пытаются оптимизировать.
  • Способ подключения и отправки сетевых данных. Возможно, в некоторых случаях вы предпочли бы подключаться и отправлять UDP-дейтаграммы, возможно, в других ситуациях, где производительность была меньше фактора, который вы отправляете с использованием TCP/IP.
  • Стратегия форматирования/сериализации данных. Разрешить коду определять, должен ли объект быть сериализован с помощью Json или с Xml. Может быть, для машин, а другой для понятных человеку ситуаций. Обе стратегии имеют метод "Сериализация", который принимает объект. Каждый сериализуется по-разному.

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

Теперь почему это было бы более полезно, чем что-то вроде:

void DoIt()
{
    if (... situation1...)
    {
       DoA()
    }
    else
    {
       DoB();
    }
}

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

DoItStrategy MakeDoItStrategy()
{
     if (... situation1...)
     {
           return new DoItStrategyA();
     }
     else
     {
           return new DoItStrategyB();
     }
}

В последнем примере вы можете просто сохранить стратегию, передав ее как еще один объект, реализующий интерфейс стратегии. Для тех, кто выполняет стратегию, у них просто есть способ выполнить действие. Они не знают, что внутренняя работа под капотом, только то, что интерфейс будет удовлетворен. Пользователям стратегии не нужно знать, почему мы приняли решение. Им просто нужно сделать какое-то действие. Мы принимаем решение один раз и передаем стратегию классам, использующим стратегию.

Например, рассмотрим случай, когда мы принимаем решение общесистемного решения на основе заданной сетевой конфигурации для подключения и отправки данных на удаленные хосты с UDP. Вместо того, чтобы каждый пользователь сетевого интерфейса нуждался в логике для принятия решения (функция "DoIt" выше), мы можем заранее разработать стратегию UDP и передать ее всем, кому необходимо отправить сетевые данные. Затем эта стратегия реализует простой интерфейс с тем же конечным результатом - данные получают от A до B.

Ответ 3

Фрагмент кода, который вы цитируете, немного обманчив, поскольку он (слегка) выходит из контекста. То, что вы пишете ниже в своем примере, также является шаблоном стратегии - вы только что переписали приведенный выше пример немного сжато.

Главное в примере - то, что специфика математической операции абстрагируется от вызывающего. Таким образом, вызывающий может работать с любым двоичным оператором, создавая новую ConcreteStrategy, например.

  int mod = new ConcreteStrategy(){ 
         public int execute(int a, int b){ return a %b; }
  }.execute(3,4);

Ответ 4

Да, пример хромает, но концепция делегатов с различными стратегиями реализации - это простой, очень старый шаблон дизайна, который я использовал во многих и многих приложениях.

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

Ответ 5

Фреймворки Cocoa, используемые на Mac и iPhone, часто используют шаблон стратегии, за исключением того, что мы называем его шаблоном делегата. Вот как мы его используем:

У нас есть конкретный объект, скажем, NSTableView. В представлении таблицы необходимо знать, сколько строк у него есть, что входит в каждую строку, каждый столбец и т.д. Поэтому вместо подкласса tableview для предоставления этой информации мы предоставляем объект "делегировать". Этот объект делегирования реализует определенный интерфейс ( "протокол" в Objective-C). Затем tableview может просто спросить свой объект-делегат, что он должен делать в определенных ситуациях ( "сколько у меня строк?" "Что происходит в этой ячейке?" "Разрешено ли пользователю выбирать эту строку?" ). Мы можем поменять объект делегата во время выполнения, просто назначив новый объект, соответствующий протоколу NSTableViewDelegate.

Итак, шаблон стратегии - один из моих любимых и тот, который я использую каждый день.

Ответ 6

Шаблон стратегии полезен в ситуациях, когда вы (или пользователи вашего кода) можете изменить вычисления в ваших алгоритмах. Простым примером, в котором я использовал шаблон стратегии, является эвристика моделирования при поиске A *. A * использует эвристику, которые являются простыми вычислениями для оценки оставшейся стоимости, если выбран определенный node (Ni) на пути к цели node (Ng). Мой интерфейс выглядел примерно так:

class Heuristic {
public:
    virtual int estimateRemainingCost(const node &Ni, const node &Ng) const = 0;
};

И они используются следующим образом:

...
// for each node (Ni) that is adjacent to the current node Nc
int node_priority = cost(Ni)/* the cost of choosing this node on the path */
                    + heuristic->estimateRemainingCost(Ni, Ng);
unsearched_nodes_.queue(node_priority, Ni);
...

Эвристика - это стратегии или вычисления, которые можно заменить. Другими словами, это тривиальное упражнение по изменению эвристики алгоритма поиска.

Ответ 7

Я обычно использую шаблон стратегии, когда у меня есть куча разных вещей, которые нужно делать, исходя из ситуации. По сути, это способ преобразования длинной последовательности операторов if/else в несколько строк.

Один из способов сделать это (на Java):

Map<String, Strategy> strategyMap = new HashMap<String, Strategy>();
strategyMap.put("bark", new BarkingStrategy());
strategyMap.put("meow", new MeowingStrategy());
strategyMap.put("moo", new MooingStrategy());
strategyMap.put("giraffeSound", new UnknownStrategy());

Сначала вы создаете какую-то форму репертуара стратегий.

Далее...

String command = //...some form of input
strategyMap.get(command).execute();

Таким образом, вы можете "в целом" обрабатывать множество разных ситуаций.

то есть:.

moo

выполнит MooingStrategy()

Ответ 8

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

Ответ 9

Это имеет смысл, когда объект контекста имеет больше обязанностей, а абстракция стратегии отделяет эти обязанности от некоторого аспекта операции. Одним из примеров (в С#) является интерфейс IComparer:

interface IComparer<T>
{
    int Compare(T a, T b);
}

Который может быть передан в алгоритм сортировки.

Ответ 10

Основное отличие состоит в том, что во втором примере стратегия - это алгоритм (следовательно, не шаблон). В первом примере вы абстрагируетесь/изолируете часть алгоритма.

Например, реализация Context.executeStrategy() может быть:

public int executeStrategy(int baseValue, int exponentFactor)
{
    return (int) Math.Pow(baseValue, this.Strategy.Calculate(2, exponentFactor));
}