|
|
Line 1: |
Line 1: |
− | In [[computer programming]], the '''strategy pattern''' is a particular [[design pattern (computer science)|software design pattern]], whereby [[algorithm]]s can be selected on-the-fly at runtime.
| |
| | | |
− | In some programming languages, such as those without [[Polymorphism (computer science)|polymorphism]], the issues addressed by this pattern are handled through forms of [[Reflection (computer science)|reflection]], such as the native function [[pointer]] or function [[Delegation (programming)|delegate]] syntax.
| |
− |
| |
− | The strategy pattern is useful for situations where it is necessary to dynamically swap the algorithms used in an application. The strategy pattern is intended to provide a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them.
| |
− |
| |
− | ==Diagram==
| |
− | <pre>
| |
− | +-------------------+ +---------------------+
| |
− | | Context | (Containment) | <<Interface>> |
| |
− | |-------------------|<>-------------| Strategy |
| |
− | |-------------------| |---------------------|
| |
− | |+ContextInterface()| |---------------------|
| |
− | +-------------------+ +----|>|+AlgorithmInterface()|<|---+
| |
− | | +---------------------+ |
| |
− | | |
| |
− | | |
| |
− | (Implements)| (Implements)|
| |
− | | |
| |
− | +---------------------+ +---------------------+
| |
− | | ConcreteStrategyA | | ConcreteStrategyB |
| |
− | |---------------------| |---------------------|
| |
− | |---------------------| |---------------------|
| |
− | |+AlgorithmInterface()| |+AlgorithmInterface()|
| |
− | +---------------------+ +---------------------+
| |
− |
| |
− | </pre>
| |
− |
| |
− | == Code Examples ==
| |
− | === [[C Sharp|C#]] ===
| |
− |
| |
− | <pre>
| |
− | using System;
| |
− |
| |
− | namespace Wikipedia.Patterns.Strategy
| |
− | {
| |
− | // MainApp test application
| |
− | class MainApp
| |
− | {
| |
− | static void Main()
| |
− | {
| |
− | Context context;
| |
− |
| |
− | // Three contexts following different strategies
| |
− | context = new Context(new ConcreteStrategyA());
| |
− | context.Execute();
| |
− |
| |
− | context = new Context(new ConcreteStrategyB());
| |
− | context.Execute();
| |
− |
| |
− | context = new Context(new ConcreteStrategyC());
| |
− | context.Execute();
| |
− | }
| |
− | }
| |
− |
| |
− | // The classes that implement a concrete strategy should implement this
| |
− | // The context class uses this to call the concrete strategy
| |
− | interface IStrategy
| |
− | {
| |
− | void Execute();
| |
− | }
| |
− |
| |
− | // Implements the algorithm using the strategy interface
| |
− | class ConcreteStrategyA : IStrategy
| |
− | {
| |
− | public void Execute()
| |
− | {
| |
− | Console.WriteLine( "Called ConcreteStrategyA.Execute()" );
| |
− | }
| |
− | }
| |
− |
| |
− | class ConcreteStrategyB : IStrategy
| |
− | {
| |
− | public void Execute()
| |
− | {
| |
− | Console.WriteLine( "Called ConcreteStrategyB.Execute()" );
| |
− | }
| |
− | }
| |
− |
| |
− | class ConcreteStrategyC : IStrategy
| |
− | {
| |
− | public void Execute()
| |
− | {
| |
− | Console.WriteLine( "Called ConcreteStrategyC.Execute()" );
| |
− | }
| |
− | }
| |
− |
| |
− | // Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
| |
− | class Context
| |
− | {
| |
− | IStrategy strategy;
| |
− |
| |
− | // Constructor
| |
− | public Context(IStrategy strategy)
| |
− | {
| |
− | this.strategy = strategy;
| |
− | }
| |
− |
| |
− | public void Execute()
| |
− | {
| |
− | strategy.Execute();
| |
− | }
| |
− | }
| |
− | }
| |
− | </pre>
| |
− |
| |
− |
| |
− |
| |
− | === [[C++]] ===
| |
− | Imagine class ''Strategy'' is an interface for a sorting-algorithm.
| |
− | Then class ''ConcreteStrategyA'' could be named ''BubbleSort'' and class ''ConcreteStrategyB'' could be named ''QuickSort''.
| |
− | The class ''Context'' could be named ''ArraySorter'' if one only needs to sort arrays. The ''context'' basically gives a hint on the problem domain - in this case an array sorting-machine.
| |
− |
| |
− | So the Strategy pattern gives the client the option to choose between sorting-routines. While the Strategy pattern implements polymorphic behavior of the "big picture", the [[Template method pattern]] implements polymorphic behavior of the "details". Think of a sorting-algorithm that is able to sort in descending and ascending order.
| |
− |
| |
− | The Strategy pattern is suited for implementing polymorphic behavior of the details of algorithms as well. Extending the polymorphic behaviour won't break communication between objects like the way the Template method pattern could. This could happen in the case of a radical expansion of the ''detail-classes''. But using the Strategy pattern for polymorphic details means one needs to maintain four classes. The Template method pattern needs one class less, so the overhead may be less as well.
| |
− |
| |
− | C++ example of two sorting-algorithms both able to sort in ascending and descending order following below. Bold names like '''Sorter.h''' and '''Sorter.cpp''' are the filenames to be included in the C++ project. They are not part of the code.
| |
− |
| |
− | The code is lengthy, and is split up into header and implementation files.
| |
− |
| |
− | '''ConsoleClient.cpp'''
| |
− |
| |
− | By default this file is called "main.cpp". This is the driver of the program. It should demonstrate in a blink of an eye the interchangability of the algorithms and the implementations (sorting by ascending or descending order).
| |
− |
| |
− | <pre>
| |
− | #include "Sorter.h"
| |
− | #include "Order.h"
| |
− | #include "Ascending.h"
| |
− | #include "Descending.h"
| |
− | #include "BubbleSort.h"
| |
− | #include "CombSort.h"
| |
− | #include <iostream>
| |
− | #include <string>
| |
− | #include <cstddef>
| |
− |
| |
− | using namespace std;
| |
− |
| |
− | void main()
| |
− | {
| |
− | string sLeaderTypes[7] = { "President", "Queen", "Warlord", "Caesar",
| |
− | "Prime Minister", "Emperor", "Archduke" };
| |
− | const size_t size = sizeof sLeaderTypes / sizeof *sLeaderTypes;
| |
− |
| |
− | Order* Ord[2];
| |
− | Ord[0] = new Ascending;
| |
− | Ord[1] = new Descending;
| |
− |
| |
− | Sorter* S[4]; //2 Sorters x 2 Orderings = 4
| |
− | S[0] = new BubbleSort(sLeaderTypes, size, Ord[0]); // BubbleSort sorting in ascending order.
| |
− | S[1] = new BubbleSort(sLeaderTypes, size, Ord[1]); // BubbleSort sorting in descending order.
| |
− | S[2] = new CombSort(sLeaderTypes, size, Ord[0]); // CombSort sorting in ascending order.
| |
− | S[3] = new CombSort(sLeaderTypes, size, Ord[1]); // CombSort sorting in descending order.
| |
− |
| |
− | for ( int i = 0; i < 4; i++ ) {
| |
− | S[i]->Sort();
| |
− | for ( int k = 0; k < size; k++ )
| |
− | cout << sLeaderTypes[k] << endl;
| |
− | cout << endl;
| |
− | delete S[i];
| |
− | }
| |
− | delete Ord[0]; delete Ord[1];
| |
− | }
| |
− | </pre>
| |
− |
| |
− | '''Sorter.h'''
| |
− |
| |
− | Base class for the the two sorting algorithms: QuickSort and CombSort.
| |
− |
| |
− | <pre>
| |
− | #ifndef ABSTRACTION_H_HEADER_INCLUDED_BB5208E4
| |
− | #define ABSTRACTION_H_HEADER_INCLUDED_BB5208E4
| |
− |
| |
− | #include "Order.h"
| |
− | #include <string>
| |
− | #include <cstddef>
| |
− |
| |
− | //From the bridge design pattern
| |
− |
| |
− | class Sorter
| |
− | {
| |
− | public:
| |
− | // Constructor
| |
− | Sorter(string*, const size_t, Order*);
| |
− |
| |
− | // abstract function
| |
− | virtual void Sort() = 0;
| |
− |
| |
− | protected:
| |
− | /*virtual*/ void Swap(string&, string&);
| |
− | /*virtual*/ bool OutOfOrder(const string&, const string&);
| |
− |
| |
− | string* _Array;
| |
− | const size_t _Size;
| |
− |
| |
− | private:
| |
− | Order* _Order;
| |
− | };
| |
− |
| |
− | #endif // ABSTRACTION_H_HEADER_INCLUDED_BB5208E4
| |
− | </pre>
| |
− |
| |
− | '''Sorter.cpp'''
| |
− |
| |
− | <pre>
| |
− | #include "Sorter.h"
| |
− |
| |
− | Sorter::Sorter(string* array, const size_t size, Order* ord) :
| |
− | _Array(array), _Size(size), _Order(ord)
| |
− | {}
| |
− |
| |
− | void Sorter::Swap(string& a, string& b)
| |
− | {
| |
− | _Order->Swap(a, b);
| |
− | }
| |
− |
| |
− | bool Sorter::OutOfOrder(const string& a, const string& b)
| |
− | {
| |
− | return _Order->OutOfOrder(a, b);
| |
− | }
| |
− | </pre>
| |
− |
| |
− | '''BubbleSort.h'''
| |
− |
| |
− | <pre>
| |
− | #ifndef REFINEDABSTRACTION_H_HEADER_INCLUDED_BB5223FD
| |
− | #define REFINEDABSTRACTION_H_HEADER_INCLUDED_BB5223FD
| |
− |
| |
− | #include "Sorter.h"
| |
− |
| |
− | class BubbleSort : public Sorter
| |
− | {
| |
− | public:
| |
− | BubbleSort(string*, const size_t, Order*);
| |
− | void Sort();
| |
− | };
| |
− |
| |
− | #endif // REFINEDABSTRACTION_H_HEADER_INCLUDED_BB5223FD
| |
− | </pre>
| |
− |
| |
− | '''BubbleSort.cpp'''
| |
− |
| |
− | <pre>
| |
− | #include "BubbleSort.h"
| |
− |
| |
− | BubbleSort::BubbleSort(string* array, const size_t size, Order* ord) :
| |
− | Sorter(array, size, ord)
| |
− | {}
| |
− |
| |
− | void BubbleSort::Sort()
| |
− | {
| |
− | for ( size_t i = _Size; i-- > 0; ) {
| |
− | for ( size_t j = 0; j < _Size-1; j++ ) {
| |
− | if ( OutOfOrder(_Array[j], _Array[j+1]) )
| |
− | Swap( _Array[j], _Array[j+1] );
| |
− | }
| |
− | }
| |
− | }
| |
− | </pre>
| |
− |
| |
− | '''CombSort.h'''
| |
− |
| |
− | <pre>
| |
− | #ifndef COMBSORT_H_HEADER_INCLUDED_BB5270CA
| |
− | #define COMBSORT_H_HEADER_INCLUDED_BB5270CA
| |
− |
| |
− | #include "Sorter.h"
| |
− |
| |
− | class CombSort : public Sorter
| |
− | {
| |
− | public:
| |
− | CombSort(string*, const size_t, Order*);
| |
− | void Sort();
| |
− |
| |
− | private:
| |
− | static int NewGap(int);
| |
− | };
| |
− |
| |
− | #endif // COMBSORT_H_HEADER_INCLUDED_BB5270CA
| |
− | </pre>
| |
− |
| |
− | '''CombSort.cpp'''
| |
− |
| |
− | <pre>
| |
− | #include "CombSort.h"
| |
− |
| |
− | CombSort::CombSort(string* array, const size_t size, Order* ord) :
| |
− | Sorter(array, size, ord)
| |
− | {}
| |
− |
| |
− |
| |
− | void CombSort::Sort()
| |
− | {
| |
− | unsigned Size = static_cast<unsigned>( _Size );
| |
− | unsigned gap = Size;
| |
− | while(true) {
| |
− | gap = NewGap(gap);
| |
− | bool swapped = false;
| |
− | for ( unsigned j = 0; j < Size-gap; j++ ) {
| |
− | if( OutOfOrder(_Array[j], _Array[j+gap]) )
| |
− | {
| |
− | Swap( _Array[j], _Array[j+gap] );
| |
− | swapped = true;
| |
− | }
| |
− | }
| |
− | if ( gap == 1 && !swapped )
| |
− | break;
| |
− | }
| |
− | }
| |
− |
| |
− |
| |
− | int CombSort::NewGap(int gap)
| |
− | {
| |
− | gap = (gap * 10) / 13;
| |
− | if (gap == 9 || gap == 10)
| |
− | gap = 11;
| |
− | if (gap < 1)
| |
− | gap = 1;
| |
− | return gap;
| |
− | }
| |
− | </pre>
| |
− |
| |
− | '''Order.h'''
| |
− |
| |
− | <pre>
| |
− | #ifndef IMPLEMENTOR_H_HEADER_INCLUDED_BB522635
| |
− | #define IMPLEMENTOR_H_HEADER_INCLUDED_BB522635
| |
− |
| |
− | #include <string>
| |
− |
| |
− | //From the bridge design pattern
| |
− |
| |
− | class Order
| |
− | {
| |
− | public:
| |
− | // Constructor
| |
− | Order();
| |
− | virtual bool OutOfOrder(const string&, const string&) const = 0;
| |
− | virtual void Swap(string&, string&);
| |
− | };
| |
− |
| |
− |
| |
− | #endif // IMPLEMENTOR_H_HEADER_INCLUDED_BB522635
| |
− | </pre>
| |
− |
| |
− | '''Order.cpp'''
| |
− |
| |
− | <pre>
| |
− | #include "Order.h"
| |
− |
| |
− | void Order::Swap(string& a, string& b)
| |
− | {
| |
− | string tmp = a;
| |
− | a = b;
| |
− | b = tmp;
| |
− | }
| |
− |
| |
− |
| |
− | Order::Order()
| |
− | {}
| |
− | </pre>
| |
− |
| |
− | '''Ascending.h'''
| |
− |
| |
− | <pre>
| |
− | #ifndef CONCRETEIMPLEMENTORA_H_HEADER_INCLUDED_BB523269
| |
− | #define CONCRETEIMPLEMENTORA_H_HEADER_INCLUDED_BB523269
| |
− |
| |
− | #include "Order.h"
| |
− | #include <string>
| |
− |
| |
− | class Ascending : public Order
| |
− | {
| |
− | public:
| |
− | bool OutOfOrder(const string&, const string&) const;
| |
− | };
| |
− |
| |
− | #endif // CONCRETEIMPLEMENTORA_H_HEADER_INCLUDED_BB523269
| |
− | </pre>
| |
− |
| |
− | '''Ascending.cpp'''
| |
− |
| |
− | <pre>
| |
− | #include "Ascending.h"
| |
− |
| |
− | bool Ascending::OutOfOrder(const string& a, const string& b) const
| |
− | {
| |
− | return a > b;
| |
− | }
| |
− | </pre>
| |
− |
| |
− | '''Descending.h'''
| |
− |
| |
− | <pre>
| |
− | #ifndef CONCRETEIMPLEMENTORB_H_HEADER_INCLUDED_BB521380
| |
− | #define CONCRETEIMPLEMENTORB_H_HEADER_INCLUDED_BB521380
| |
− |
| |
− | #include "Order.h"
| |
− | #include <string>
| |
− |
| |
− | class Descending : public Order
| |
− | {
| |
− | public:
| |
− | bool OutOfOrder(const string&, const string&) const;
| |
− | };
| |
− |
| |
− | #endif // CONCRETEIMPLEMENTORB_H_HEADER_INCLUDED_BB521380
| |
− | </pre>
| |
− |
| |
− | '''Descending.cpp'''
| |
− |
| |
− | <pre>
| |
− | #include "Descending.h"
| |
− |
| |
− | bool Descending::OutOfOrder(const string& a, const string& b) const
| |
− | {
| |
− | return a < b;
| |
− | }
| |
− | </pre>
| |