Когда использовать шаблонный метод Vs. Стратегия? - PullRequest
26 голосов
/ 23 марта 2009

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

Ответы [ 11 ]

32 голосов
/ 23 марта 2009

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

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

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

18 голосов
/ 02 апреля 2009

На самом деле они могут использоваться вместе довольно эффективно.

Не думайте, что шаблоны - это рецепты с конкретным кодом для их реализации.

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

Шаблонный метод дает вам «алгоритм со сменными шагами». (Алгоритм обычно определяется не переопределяемым методом (например, final или private))

Реализация этой концепции в GoF для замены этих шагов использует наследование и переопределение методов.

Однако , вы все еще используете метод Template, если эти шаги заменены на стратегии .

Например, подумайте о классе, который хочет пройтись по двоичному порядку дерева и «сделать что-то» на каждом узле.

Намерение состоит в том, что метод inorder () является шаблонным методом - структура прогулки всегда одинакова.

Метод «ловушки», часть, которая «что-то делает», может быть реализована как метод в том же классе (и переопределен в подклассах для изменения поведения), так и внешне, и в этом случае это стратегия «делать что-то» .

18 голосов
/ 23 марта 2009

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

Во всех других случаях (т. Е. Когда алгоритму нужно только использовать интерфейс объекта), я пытаюсь использовать стратегию.

Кроме того, стратегия полезна только в том случае, если существуют реальные алгоритмы для реализации: если единственное различие между классами заключается в том, например, какое простое значение вернуть, используйте метод Template.

13 голосов
/ 23 марта 2009

Рассмотрим стратегию использования, когда:

  • Необходимо изменить поведение вашего объекта во время выполнения.
  • У вас уже есть иерархия классов по другим критериям.
  • Вы хотите разделить логику стратегии между различными классами.

В других случаях следует использовать шаблон шаблона.

6 голосов
/ 09 марта 2012

Я не согласен с этим утверждением (из этого ответа ):

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


Если вы ХОТИТЕ, чтобы люди унаследовали от вашего класса, тогда вы ХОТИТЕ конкретную реализацию, а не хотите конкретного поведения. Это плохо пахнет для меня.

Действительной вещью для WANT является способность переопределять или предоставлять реализации отдельных шагов алгоритма. Эта цель может быть достигнута как с помощью методов шаблонов (где мы можем выборочно переопределять защищенные методы), так и с помощью шаблона стратегии (где мы внедряем реализации).

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

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

Я бы никогда не начал с "Я хочу позволить им наследовать от этого класса". Это телега перед лошадью ИМО.

5 голосов
/ 23 марта 2009

Вы можете создать большое дерево наследования просто для изменения одного из N поведения. И вы можете создать второе большое дерево наследования, чтобы изменить второе из N поведения.

Но вы также можете разгрузить свое дерево, создав маленькие деревья стратегий.

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

3 голосов
/ 11 апреля 2011

Я бы хотел согласиться и дать второе объяснение Скотту.

Шаблон pattern = заботится о том, чтобы нарисовать общие линии, вдоль которых будет выполняться операция - шаблонизацию - в основном "алгоритм со сменными шагами" (очень хорошо придуманный), где сменные шаги могут быть делегированы с использованием Стратегия шаблонная концепция.

Стратегия pattern = заботится только о том, чтобы отделить клиента от подчеркивающей реализации операции, результат которой должен всегда подчиняться некоторым заранее определенным правилам (например, сортировка, где результат всегда является отсортированным списком, но вы можете отложить фактическая сортировка в пузырьковую или быструю сортировку).

Приветствие.

2 голосов
/ 23 марта 2009

Один из центральных принципов OO Design - это «Favor Composition for Inheritance», так что это говорит в пользу модели Стратегии. Очевидно, это зависит от того, чего вы пытаетесь достичь в конкретном сценарии.

0 голосов
/ 02 марта 2016

Я бы предпочел использовать оба варианта: выгрузить реализацию по умолчанию (из шаблона Template) в класс Context шаблона стратегии. Таким образом, я могу заставить пользователя вызывать метод, который я хочу, чтобы он вызывал так, чтобы порядок выполнения шагов алгоритма оставался под контролем.

 /**
 *  enables replaceable steps in algorithm
 */
public interface HouseStrategy{
      void buildWalls();
      void buildPillars();
}


public class HouseContext{


    //public API that enforces order of execution
    public void build(HouseStrategy strategy){
        buildFoundation();//default implementation
        strategy.buildPillars();//delegated to concrete strategy
        strategy.buildWalls();//delegated to concrete strategy
        buildWindows();//default implementation
    }

    //default implementation
    private void buildWindows() {
        System.out.println("Building Glass Windows");
    }
    //default implementation
    private void buildFoundation() {
        System.out.println("Building foundation with cement,iron rods and sand");
    }

}

public class WoodenHouse implements HouseStrategy {

    @Override
    public void buildWalls() {
        System.out.println("Building Wooden Walls");
    }

    @Override
    public void buildPillars() {
        System.out.println("Building Pillars with Wood coating");
    }

}

public class GlassHouse implements HouseStrategy {

    @Override
    public void buildWalls() {
        System.out.println("Building Wooden Of glass");
    }

    @Override
    public void buildPillars() {
        System.out.println("Building Pillars with glass coating");
    }

}

Как мы видим, конкретные стратегии все еще открыты для расширения. Как в,

public class GlassHouse implements HouseStrategy,EarthquakeResistantHouseStrategy{......}

Использование

HouseContext context = new HouseContext();

    WoodenHouse woodenHouseStrategy = new WoodenHouse();
    context.build(woodenHouseStrategy);

    GlassHouse glassHouseStrategy = new GlassHouse();
    context.build(glassHouseStrategy);

Один недостаток, который я вижу здесь, заключается в том, что конкретные стратегии могут изменять только вариантное поведение алгоритма, то есть buildWalls () и buildPillars (). Если нам нужно изменить инвариантные части, то есть buildFoundation () и buildWindows (), нам нужно создать другой класс Context, реализующий новое поведение. Тем не менее, мы получаем возможность повторного использования кода, которого нет в чистом шаблоне стратегии: -)

0 голосов
/ 11 февраля 2014

Мое резюме: шаблон стратегии связан более свободно, чем шаблонный метод, что, как правило, хорошо.

Роберт К. Мартин в МЕТОД ШАБЛОНОВ И СТРАТЕГИЯ: Наследование против делегирования

Таким образом, шаблон СТРАТЕГИЯ обеспечивает одно дополнительное преимущество по сравнению с ШАБЛОН МЕТОД шаблон. В то время как шаблон ШАБЛОН МЕТОД позволяет универсальный алгоритм для манипулирования многими возможными подробными реализации, полностью соответствуя шаблону DIP СТРАТЕГИЯ дополнительно позволяет манипулировать каждой подробной реализацией много разных общих алгоритмов.

DIP - это принцип обращения зависимостей:

A. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций. Б. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

( Википедия и Мартин снова ).

...