Jaka jest różnica między metodą szablonową a wzorcami strategii?


161

Czy ktoś może mi wyjaśnić, jaka jest różnica między wzorcem metody szablonowej a wzorcem strategii?

O ile wiem, są one w 99% takie same - jedyną różnicą jest to, że wzorzec metody szablonu ma klasę abstrakcyjną jako klasę bazową, podczas gdy klasa strategii używa interfejsu implementowanego przez każdą konkretną klasę strategii.

Jednak z punktu widzenia klienta są one konsumowane dokładnie w ten sam sposób - czy to prawda?


2
Ten post w SO ma lepszą odpowiedź na to samo pytanie: stackoverflow.com/questions/464524/ ...
Gob00st

12
Pytanie, z którym łączy się gob00st, to różnica między strategią a mostem. To wcale nie jest odpowiedź na to pytanie.
bluekeys

Odpowiedzi:


135

Główna różnica między nimi polega na wybraniu konkretnego algorytmu.

W przypadku wzorca metody Template dzieje się to w czasie kompilacji przez podklasę szablonu. Każda podklasa zapewnia inny konkretny algorytm, implementując abstrakcyjne metody szablonu. Gdy klient wywołuje metody interfejsu zewnętrznego szablonu, szablon wywołuje swoje metody abstrakcyjne (jego interfejs wewnętrzny) zgodnie z wymogami wywołania algorytmu.

class ConcreteAlgorithm : AbstractTemplate
{
    void DoAlgorithm(int datum) {...}
}

class AbstractTemplate
{
    void run(int datum) { DoAlgorithm(datum); }

    virtual void DoAlgorithm() = 0; // abstract
}

W przeciwieństwie do tego wzorzec strategii pozwala na wybór algorytmu w czasie wykonywania przez zawieranie . Konkretne algorytmy są implementowane przez oddzielne klasy lub funkcje, które są przekazywane do strategii jako parametr do jej konstruktora lub do metody ustawiającej. Algorytm wybrany dla tego parametru może zmieniać się dynamicznie w zależności od stanu programu lub danych wejściowych.

class ConcreteAlgorithm : IAlgorithm
{
    void DoAlgorithm(int datum) {...}
}

class Strategy
{
    Strategy(IAlgorithm algo) {...}

    void run(int datum) { this->algo.DoAlgorithm(datum); }
}

W podsumowaniu:

  • Wzorzec metody szablonu: wybór algorytmu w czasie kompilacji przez podklasy
  • Wzorzec strategii: wybór algorytmu w czasie wykonywania przez zawieranie

47
Oba wzorce obsługują wybór używanego algorytmu w czasie wykonywania (w przypadku metody szablonu należy zrobić coś podobnego if (config.useAlgoA) impl = new AlgoA() else impl = new AlgoB()), więc ta odpowiedź jest niepoprawna.
Borek Bernard

13
Jasne, że możesz to zrobić, ale wtedy nie używasz wzorca szablonu. W rzeczywistości prawie dokładnie tak będzie wyglądał kod tworzący instancję Strategy!
dom

21
-1, myślę, że ta odpowiedź (choć niezupełnie błędna) mija się z punktem, w którym są rzeczywiste różnice. Odpowiedź @ tvanfosson jest znacznie lepsza.
Doc Brown

1
@Karoly Nyisztor Obaj mogą „zastępować zachowanie” i „zapewniać punkty rozszerzenia”. Niezależnie od tego, czy coś jest zachowaniem, czy rozszerzeniem, tak naprawdę zależy to od kontekstu, w którym zastosujesz dany wzorzec. Możesz również nazwać każdą podklasę wzorca metody szablonu „strategią” lub nazwać każdą klasę strategii we wzorcu strategii „rozszerzeniem”, to tylko sformułowanie. Faktem jest, że robią to samo, Z WYJĄTKIEM różnicy, o której wspomina ta odpowiedź. Więc to jest właściwa odpowiedź.
Andy

1
Konkretny algorytm jest wybierany w ten sam sposób dla obu wzorców. Wyboru dokonuje się poprzez przywołanie new ConcreteAlgorithm1()versus new ConcreteAlgorithm2(). Oczywiście wybór następuje w czasie wykonywania (wybranie algorytmu w czasie kompilacji oznaczałoby zakodowanie go na stałe). Główną różnicą między nimi jest sposób implementacji konkretnego algorytmu. Czy jest zaimplementowany jako podklasa czy jako oddzielny interfejs? Pierwsza z nich to szablon. Ta ostatnia to strategia. Różnicę można podsumować jako kompozycję a dziedziczenie, co jest wspólnym tematem książki GoF.
jaco0646

138

Wzorzec szablonu jest używany, gdy dana operacja ma pewne niezmienne zachowanie, które można zdefiniować w kategoriach innych różnych zachowań pierwotnych. Klasa abstrakcyjna definiuje niezmienne zachowanie, podczas gdy klasy implementujące definiują metody zależne.

W strategii implementacje zachowania są niezależne - każda klasa implementująca definiuje zachowanie i nie ma między nimi współdzielonego kodu. Oba są wzorcami behawioralnymi i jako takie są konsumowane w podobny sposób przez klientów. Zwykle strategie mają jedną metodę publiczną - execute()metodę, podczas gdy szablony mogą definiować zestaw metod publicznych, a także zestaw wspierających prymitywów prywatnych, które muszą implementować podklasy.

Te dwa wzory można z łatwością wykorzystać razem. Możesz mieć wzorzec strategii, w którym kilka implementacji należy do rodziny strategii zaimplementowanych przy użyciu wzorca szablonu.


Wydaje mi się to słuszne, ale dlaczego WikiPedia wspomina, że ​​„wzorzec strategii służy do wybierania zachowania algorytmu w czasie wykonywania”? Może być również użyty do wyboru zachowania algorytmu w czasie kompilacji, podobnie jak metoda szablonowa? Czy coś mi brakuje?
BornToCode

2
@BornToCode Zakładam, że to, o czym mówią, to wybór określonej strategii w czasie wykonywania. Na przykład istnieje kilka sposobów numerycznego znajdowania pierwiastków równania. W zależności od dziedziny problemu lub danych możesz wybrać strategię Newtona-Raphsona, Eulera lub inną strategię rozwiązania równania. Każdy z nich to strategia. Większy algorytm, którego jedną częścią jest rozwiązanie równania, wybiera strategię do zastosowania na podstawie pewnej jakości problemu.
tvanfosson

Tak, ale to nie jest tak, że wzorzec strategii powinien być używany TYLKO w takich przypadkach? Chodzi mi o to, że jeśli muszę tylko wybrać zachowanie algorytmu w czasie kompilacji, czy powinienem nadal używać wzorca strategii, czy też nie powinien być używany w ten sposób?
BornToCode

1
@BornToCode Powiedziałbym, że strategia jest najbardziej przydatna, gdy wybór jest dynamiczny. Szablon jest w zasadzie sposobem tworzenia różnych, powiązanych zachowań dla znanych plików. Użyłbyś jakiejś strategii (choć niekoniecznie wzorca strategii), aby wybrać zachowanie oparte na szablonie. Na przykład dziedziczenie produktów - tworzysz produkt podstawowy, dodajesz funkcje dla różnych produktów. Wybór typu produktu (klasy) do utworzenia wystąpienia może zależeć od tego, z jakich tabel / widoków jest ładowany. Wzorzec strategii tak naprawdę nie ma tam znaczenia.
tvanfosson

2
@BornToCode to nie jest albo / albo rzecz, to tak-i. Zastosuj wzór tam, gdzie jest to właściwe, połącz wzory tam, gdzie jest to przydatne.
tvanfosson


24

Prawdopodobnie masz na myśli wzorzec metody szablonowej. Masz rację, służą bardzo podobnym potrzebom. Powiedziałbym, że lepiej jest używać metody szablonowej w przypadkach, gdy masz algorytm „szablonu”, który ma zdefiniowane kroki, w których podklasy zastępują te kroki, aby zmienić niektóre szczegóły. W przypadku strategii musisz stworzyć interfejs, a zamiast dziedziczenia użyć delegacji. Powiedziałbym, że jest to nieco mocniejszy wzorzec i może lepszy zgodnie z DIP - zasadami inwersji zależności. Ma większą moc, ponieważ jasno definiujesz nową abstrakcję strategii - sposób robienia czegoś, co nie dotyczy metody szablonowej. Jeśli więc ta abstrakcja ma sens - wykorzystaj ją. Jednak użycie metody szablonowej może dać prostsze projekty w prostych przypadkach, co również jest ważne. Zastanów się, które słowa pasują lepiej: czy masz algorytm szablonu? A może kluczowe jest tutaj to, że masz abstrakcję strategii - nowy sposób robienia czegoś

Przykład metody szablonowej:

Application.main()
{
Init();
Run();
Done();
}

Tutaj dziedziczysz z aplikacji i zastępujesz to, co dokładnie zostanie zrobione podczas init, run i done.

Przykład strategii:

array.sort (IComparer<T> comparer)

Tutaj, pisząc funkcję porównującą, nie dziedziczysz z tablicy. Array deleguje algorytm porównania do funkcji porównującej.


3
Myślę, że to świetna odpowiedź
Calanus

23

Różnica między strategią i metodą szablonową


Podobieństwa

Wzorce strategii i metod szablonowych mają między sobą wiele podobieństw. Aby spełnić zasadę otwartego-zamkniętego i ułatwić rozszerzanie modułu oprogramowania bez zmiany jego kodu, można zastosować zarówno wzorce metody strategicznej, jak i szablonowej. Oba wzorce reprezentują oddzielenie ogólnej funkcjonalności od szczegółowej implementacji tej funkcjonalności. Jednak różnią się one nieco pod względem oferowanej szczegółowości.


Różnice

Oto niektóre z różnic, które zauważyłem podczas badania tych dwóch wzorców:

  1. W strategii połączenie między klientem a strategią jest bardziej luźne, podczas gdy w metodzie szablonowej oba moduły są ściślej powiązane.
  2. W strategii przeważnie używany jest interfejs, chociaż w zależności od sytuacji można również użyć klasy abstrakcyjnej, a klasa konkretna nie jest używana, podczas gdy w metodzie Template używana jest głównie klasa abstrakcyjna lub klasa konkretna, interfejs nie jest używany.
  3. We wzorcu strategii ogólnie całe zachowanie klasy jest reprezentowane za pomocą interfejsu, z drugiej strony metoda Template służy do ograniczenia powielania kodu, a kod standardowy jest definiowany w ramach podstawowej lub abstrakcyjnej. W metodzie szablonu może nawet istnieć konkretna klasa z domyślną implementacją.
  4. W prostych słowach można zmienić całą strategię (algorytm) we wzorcu Strategii, jednak w metodzie Szablon tylko niektóre rzeczy się zmieniają (części algorytmu), a reszta pozostaje niezmieniona. W metodzie szablonu niezmienne kroki są implementowane w abstrakcyjnej klasie bazowej, podczas gdy kroki wariantowe mają domyślną implementację lub nie mają żadnej implementacji. W metodzie szablonowej projektant komponentów narzuca wymagane kroki algorytmu i ich kolejność, ale pozwala klientowi składnika na rozszerzenie lub zastąpienie pewnej liczby tych kroków.

Zdjęcie pochodzi z pogryzionego bloga.



11

Obie są bardzo podobne i oba są używane przez kod klienta w podobny sposób. W przeciwieństwie do tego, co mówi najpopularniejsza odpowiedź powyżej, obie pozwalają na wybór algorytmu w czasie wykonywania .

Różnica między nimi polega na tym, że podczas gdy wzorzec strategii pozwala różnym implementacjom na użycie zupełnie różnych sposobów osiągnięcia pożądanego rezultatu, wzorzec metody szablonowej określa nadrzędny algorytm (metodę „szablonową”), który jest używany do osiągnięcia wyniku - - jedyny wybór pozostawiony konkretnym implementacjom (podklasom) to pewne szczegóły wspomnianej metody szablonowej. Odbywa się to poprzez wywołanie przez metodę szablonu jednej lub więcej metod abstrakcyjnych , które są nadpisywane (tj. Implementowane) przez podklasy, w przeciwieństwie do metody szablonu, która sama nie jest abstrakcyjna i nie jest zastępowana przez podklasy .

Kod klienta wywołuje metodę template przy użyciu referencji / wskaźnika klasy abstrakcyjnej wskazującej na wystąpienie jednej z konkretnych podklas, które można określić w czasie wykonywania, tak jak przy użyciu wzorca strategii.


9

Metoda szablonu:

  1. Opiera się na dziedziczeniu
  2. Definiuje szkielet algorytmu, którego nie można zmienić przez podklasy. W klasach podrzędnych można przesłonić tylko niektóre operacje
  3. Klasa nadrzędna całkowicie kontroluje algorytm i różni tylko niektóre kroki od konkretnych klas
  4. Wiązanie jest wykonywane w czasie kompilacji

Struktura Template_method :

wprowadź opis obrazu tutaj

Strategia:

  1. Opiera się na delegacji / kompozycji
  2. Zmienia wnętrzności obiektu poprzez modyfikację zachowania metody
  3. Służy do przełączania między rodziną algorytmów
  4. Zmienia zachowanie obiektu w czasie wykonywania, całkowicie zastępując jeden algorytm innym algorytmem w czasie wykonywania
  5. Wiązanie jest wykonywane w czasie wykonywania

Struktura strategii :

wprowadź opis obrazu tutaj

Zapoznaj się z artykułami dotyczącymi metody szablonowej i strategii, aby lepiej zrozumieć.

Powiązane posty:

Wzorzec projektu szablonu w JDK, nie można znaleźć metody definiującej zbiór metod do wykonania w kolejności

Przykład wzorca strategii w świecie rzeczywistym


3

Nie, niekoniecznie są one spożywane w ten sam sposób. Wzorzec „metoda szablonu” jest sposobem dostarczania „wskazówek” przyszłym realizatorom. Mówisz im: „Wszystkie obiekty muszą mieć numer ubezpieczenia społecznego” (to trywialny przykład, ale dobrze oddaje ideę).

Wzorzec strategii umożliwia włączanie i wyłączanie wielu możliwych implementacji. Nie jest (zwykle) implementowana poprzez dziedziczenie, ale zamiast tego pozwalając wywołującemu przejść do żądanej implementacji. Przykładem może być zezwolenie na dostarczenie ShippingCalculator jednego z kilku różnych sposobów obliczania podatków (implementacja NoSalesTax i być może implementacja PercentageBasedSalesTax).

Tak więc czasami klient w rzeczywistości powie obiektowi, której strategii ma użyć. Jak w

myShippingCalculator.CalculateTaxes(myCaliforniaSalesTaxImpl);

Ale klient nigdy nie zrobiłby tego dla obiektu opartego na metodzie szablonu. W rzeczywistości klient może nawet nie wiedzieć, że obiekt jest oparty na metodzie szablonu. Te abstrakcyjne metody we wzorcu Template Method mogą być nawet chronione, w takim przypadku klient nawet nie wiedziałby, że istnieją.


3

Proponuję przeczytać ten artykuł. Wyjaśnia różnice na prawdziwym przykładzie.

Cytat z artykułu

Jak widać, implementacja klas zależy również od klasy metody szablonu. Zależność ta powoduje zmianę metody szablonu, jeśli chce się zmienić niektóre kroki algorytmu. Z drugiej strony strategia całkowicie hermetyzuje algorytm. klasy, aby całkowicie zdefiniować algorytm. Dlatego jeśli nadejdzie jakakolwiek zmiana, konieczna jest zmiana kodu wcześniej napisanych zajęć.To był główny powód, dla którego wybrałem strategię projektowania klas.

Jedną z cech metody szablonowej jest to, że metoda szablonu kontroluje algorytm. Co może być dobre w innej sytuacji, ale w moim problemie ograniczało to mnie do projektowania zajęć. Z drugiej strony strategia nie kontroluje kroków algorytmu, dzięki czemu mogę dodać zupełnie inne metody konwersji. Stąd w moim przypadku strategia pomaga mi we wdrożeniu.

Jedną z wad strategii jest zbyt duża nadmiarowość kodu i mniejsze udostępnianie kodu. Jak widać na przedstawionym przykładzie tego artykułu, muszę wielokrotnie powtarzać ten sam kod w czterech klasach. Dlatego jest to trudne do utrzymania, ponieważ jeśli implementacja naszego systemu, takiego jak krok 4, który jest wspólny dla wszystkich, zostanie zmieniona, będę musiał zaktualizować to we wszystkich 5 klasach. Z drugiej strony, w metodzie szablonowej, mogę zmienić tylko nadklasę, a zmiany są odzwierciedlane w podklasach. Dlatego metoda szablonowa zapewnia bardzo małą redundancję i dużą ilość współużytkowania kodu między klasami.

Strategia umożliwia również zmianę algorytmu w czasie wykonywania. W metodzie szablonowej należy ponownie zainicjalizować obiekt. Ta cecha strategii zapewnia dużą elastyczność. Z punktu widzenia projektowania należy przedkładać kompozycję nad dziedziczenie. Dlatego też używanie wzorca strategii stało się podstawowym wyborem dla rozwoju ”.


2

Wzorzec szablonu jest podobny do wzorca strategii. Te dwa wzorce różnią się zakresem i metodologią.

Strategia jest używana, aby umożliwić dzwoniącym zmianę całego algorytmu, na przykład sposobu obliczania różnych rodzajów podatku, podczas gdy metoda szablonu służy do różnicowania kroków w algorytmie. Z tego powodu strategia jest gruboziarnista. Szablon umożliwia precyzyjniejsze kontrole w sekwencji operacji, a jednocześnie pozwala na różne implementacje tych szczegółów.

Inną główną różnicą jest to, że strategia używa delegowania, podczas gdy metoda szablonu korzysta z dziedziczenia. W strategii algorytm jest delegowany do innej klasy xxxStrategy, do której podmiot będzie miał odniesienie, ale za pomocą szablonu możesz podklasować podstawową i przesłonić metody w celu wprowadzenia zmian.

z http://cyruscrypt.blogspot.com/2005/07/template-vs-strategy-patterns.html


2

W strategii podklasy kierują pokazem i kontrolują algorytm. Tutaj kod jest zduplikowany we wszystkich podklasach. Znajomość algorytmu i sposobu jego implementacji jest rozłożona na wiele klas.

We wzorcu szablonu klasa bazowa ma algorytm. Maksymalizuje ponowne użycie wśród podklas. Ponieważ algorytm leży w jednym miejscu, chroni go klasa bazowa.


2

Wzorzec projektowania strategii

  • Obsługuje kompozycję.
  • Zapewnia elastyczność zmiany zachowania obiektu w czasie wykonywania.
  • Mniejsze sprzężenie między kodem klienta a kodem rozwiązania / algorytmu.

Szablon Metoda Projekt Wzorzec

  • Preferuje dziedziczenie nad kompozycją
  • Zdefiniuj algorytm w swojej klasie bazowej. Poszczególne fragmenty algorytmu można dostosowywać w klasach podrzędnych.

1

Wzór szablonu:

Metoda szablonowa polega na umożliwieniu podklasom redefiniowania pewnych kroków algorytmu, bez zmiany głównej struktury i kroków algorytmu zdefiniowanych w klasie bazowej. Wzorzec szablonu zwykle używa dziedziczenia, więc ogólna implementacja algorytmów może być dostarczona w klasie bazowej, którą podklasa może zastąpić w razie potrzeby.

public abstract class RobotTemplate {
    /* This method can be overridden by a subclass if required */
    public void start() {
        System.out.println("Starting....");
    }

    /* This method can be overridden by a subclass if required */
    public void getParts() {
        System.out.println("Getting parts....");
    }

    /* This method can be overridden by a subclass if required */
    public void assemble() {
        System.out.println("Assembling....");
    }

    /* This method can be overridden by a subclass if required */
    public void test() {
        System.out.println("Testing....");
    }

    /* This method can be overridden by a subclass if required */
    public void stop() {
        System.out.println("Stopping....");
    }

    /*
     * Template algorithm method made up of multiple steps, whose structure and
     * order of steps will not be changed by subclasses.
     */
    public final void go() {
        start();
        getParts();
        assemble();
        test();
        stop();
    }
}


/* Concrete subclass overrides template step methods as required for its use */
public class CookieRobot extends RobotTemplate {
    private String name;

    public CookieRobot(String n) {
        name = n;
    }

    @Override
    public void getParts() {
        System.out.println("Getting a flour and sugar....");
    }

    @Override
    public void assemble() {
        System.out.println("Baking a cookie....");
    }

    @Override
    public void test() {
        System.out.println("Crunching a cookie....");
    }

    public String getName() {
        return name;
    }
}

Zauważ, że w powyższym kodzie kroki algorytmu go () będą zawsze takie same, ale podklasy mogą definiować inną receptę na wykonanie określonego kroku.

Wzorzec strategii:

Wzorzec strategii polega na umożliwieniu klientowi wyboru konkretnej implementacji algorytmów w czasie wykonywania. Wszystkie algorytmy są izolowane i niezależne, ale implementują wspólny interfejs i nie ma pojęcia o definiowaniu poszczególnych kroków w algorytmie.

/**
 * This Strategy interface is implemented by all concrete objects representing an
 * algorithm(strategy), which lets us define a family of algorithms.
 */
public interface Logging {
    void write(String message);
}

/**
 * Concrete strategy class representing a particular algorithm.
 */
public class ConsoleLogging implements Logging {

    @Override
    public void write(String message) {
        System.out.println(message); 
    }

}

/**
 * Concrete strategy class representing a particular algorithm.
 */
public class FileLogging implements Logging {

    private final File toWrite;

    public FileLogging(final File toWrite) {
        this.toWrite = toWrite;
    }

    @Override
    public void write(String message) {
        try {
            final FileWriter fos = new FileWriter(toWrite);
            fos.write(message);
            fos.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }

}

Aby uzyskać pełny kod źródłowy, sprawdź moje repozytorium github .


0

Strategia jest prezentowana jako interfejs i metoda szablonu jako Klasa abstrakcyjna. Jest to zwykle często używane w frameworkach. np. klasa MessageSource platformy Spring Framework jest interfejsem strategii służącym do rozwiązywania komunikatów. Klient korzysta z określonej implementacji (strategii) tego interfejsu.

I abstrakcyjna implementacja tego samego interfejsu AbstractMessageSource, który ma wspólną implementację rozwiązywania komunikatów i ujawnia abstrakcyjną metodęolveCode (), aby podklasy mogły implementować je na swój sposób. AbstractMessageSource jest przykładem metody szablonowej.

http://docs.spring.io/spring/docs/4.1.7.RELEASE/javadoc-api/org/springframework/context/support/AbstractMessageSource.html


0

W metodzie szablonowej tego wzorca projektowego jeden lub więcej kroków algorytmu może zostać zastąpionych przez podklasy, aby umożliwić różne zachowania, zapewniając jednocześnie, że nadrzędny algorytm jest nadal przestrzegany (Wiki).

Nazwa wzorca Metoda szablonowa oznacza, czym jest. Powiedzmy, że mamy metodę CalculateSomething () i chcemy utworzyć szablon tej metody. Ta metoda zostanie zadeklarowana w klasie bazowej jako metoda niewirtualna. Powiedz, że metoda wygląda następująco.

CalculateSomething(){
    int i = 0;
    i = Step1(i);
    i++;
    if (i> 10) i = 5;
    i = Step2(i);
    return i;

} Implementacje metod Step1 i Step2 mogą być podane przez klasy pochodne.

We wzorcu strategii nie ma implementacji dostarczonej przez bazę (jest to powód, dla którego podstawa jest tak naprawdę interfejsem w diagramie klas)

Klasycznym przykładem jest sortowanie. Na podstawie liczby obiektów, które należy posortować, tworzona jest odpowiednia klasa algorytmu (merge, bubble, quick itp.), A cały algorytm jest hermetyzowany w każdej klasie.

Czy możemy teraz zaimplementować sortowanie jako metodę szablonową? Z pewnością możesz, ale nie znajdziesz zbyt wielu / żadnych wspólnych elementów do wyodrębnienia i umieszczenia w podstawowej implementacji. Więc to niweczy cel wzorca metody szablonowej.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.