Difference between revisions of "Strategy"

From CDOT Wiki
Jump to: navigation, search
 
 
(20 intermediate revisions by the same user not shown)
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.
+
__TOC__
  
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.
 
  
 +
==Defenition==
 +
Allows you to define a set of algorithms, to be used by different objects.
 +
Strategy lets the algorithm vary independently from objects that use it.
 +
 +
Objects can be assigned to algorithms on runtime.
 
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.
 
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>
+
==UML Diagram==
using System;
+
[[Image:strategy.gif]]
  
namespace Wikipedia.Patterns.Strategy
+
==Benefits and Drawbacks of using Strategy Pattern==
{
+
'''Benefits in using Strategy Pattern'''
  // MainApp test application
 
  class MainApp
 
  {
 
    static void Main()
 
    {
 
      Context context;
 
  
      // Three contexts following different strategies
+
#A family of algorithms can be defined as a class hierarchy and can be used interchangeably to alter application behavior without changing its architecture.
      context = new Context(new ConcreteStrategyA());
+
#By encapsulating the algorithm separately, new algorithms complying with the same interface can be easily introduced.
      context.Execute();
+
#The application can switch strategies at run-time.
 +
#Strategy enables the clients to choose the required algorithm, without using a "switch" statement or a series of "if-else" statements.
 +
#Data structures used for implementing the algorithm is completely encapsulated in Strategy classes. Therefore, the implementation of an algorithm can be changed without affecting the Context class.
 +
#Strategy Pattern can be used instead of sub-classing the Context class. Inheritance hardwires the behavior with the Context and the behavior cannot be changed dynamically.
 +
#The same Strategy object can be strategically shared between different Context objects. However, the shared Strategy object should not maintain states across invocations.
  
      context = new Context(new ConcreteStrategyB());
+
'''Drawbacks in using Strategy Pattern'''
      context.Execute();
 
  
      context = new Context(new ConcreteStrategyC());
+
#The application must be aware of all the strategies to select the right one for the right situation.
      context.Execute();
+
#Strategy and Context classes may be tightly coupled. The Context must supply the relevant data to the Strategy for implementing the algorithm and sometimes, all the data passed by the Context may not be relevant to all the Concrete Strategies.
    }
+
#Context and the Strategy classes normally communicate through the interface specified by the abstract Strategy base class. Strategy base class must expose interface for all the required behaviors, which some concrete Strategy classes might not implement.  
  }
+
#In most cases, the application configures the Context with the required Strategy object. Therefore, the application needs to create and maintain two objects in place of one.
 +
#Since, the Strategy object is created by the application in most cases; the Context has no control on lifetime of the Strategy object. However, the Context can make a local copy of the Strategy object. But, this increases the memory requirement and has a sure performance impact.
  
  // The classes that implement a concrete strategy should implement this
+
==Code Sample==
  // The context class uses this to call the concrete strategy
+
Strategy Method in the .NET Framework that works with ArryList.<br>
  interface IStrategy
+
By default the QuickSort algorithm is used to sort item in the list.
  {
+
Some times there is a need to use a different sort algorithm there is a overload of sort that takes an IComparer.
    void Execute();
+
<br>
  }
 
  
  // Implements the algorithm using the strategy interface
+
<br><pre>
  class ConcreteStrategyA : IStrategy
+
class CoolComparer : IComparer
  {
+
{
     public void Execute()
+
     #region IComparer Members
    {
 
      Console.WriteLine( "Called ConcreteStrategyA.Execute()" );
 
    }
 
  }
 
 
 
  class ConcreteStrategyB : IStrategy
 
  {
 
    public void Execute()
 
    {
 
      Console.WriteLine( "Called ConcreteStrategyB.Execute()" );
 
    }
 
  }
 
  
  class ConcreteStrategyC : IStrategy
+
     public int Compare(object x, object y)
  {
 
     public void Execute()
 
 
     {
 
     {
      Console.WriteLine( "Called ConcreteStrategyC.Execute()" );
+
        // TODO:  implementation
 +
        return 0;
 
     }
 
     }
  }
 
 
  // Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
 
  class Context
 
  {
 
    IStrategy strategy;
 
  
     // Constructor
+
     #endregion
    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()
+
class SlowComparer: IComparer
 
{
 
{
  string sLeaderTypes[7] = { "President", "Queen", "Warlord", "Caesar",
+
     #region IComparer Members
     "Prime Minister", "Emperor", "Archduke" };
 
  const size_t size = sizeof sLeaderTypes / sizeof *sLeaderTypes;
 
  
  Order* Ord[2];
+
     public int Compare(object x, object y)
  Ord[0] = new Ascending;
+
     {
  Ord[1] = new Descending;
+
         // TODO: implementation
 
+
        return 0;
  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;
 
  }
 
}
 
  
 +
    #endregion
  
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
+
ArrayList items = new ArrayList();
  
class Order
+
items.Add("One");
{
+
items.Add("Two");
public:
+
items.Add("Three");
  // Constructor
 
  Order();
 
  virtual bool OutOfOrder(const string&, const string&) const = 0;
 
  virtual void Swap(string&, string&);
 
};
 
  
 +
items.Sort(); // Uses IComparable on string object
  
#endif // IMPLEMENTOR_H_HEADER_INCLUDED_BB522635
+
IComparer myComparer = new CoolComparer();
 +
IComparer myComparer = new SlowComparer();
 +
items.Sort(myComparer); // Delegate Comparison Method
 
</pre>
 
</pre>
  
'''Order.cpp'''
+
==References==
 
+
http://msdn2.microsoft.com/en-us/library/system.collections.comparer(VS.80).aspx<br>
<pre>
+
http://www.codeproject.com/cpp/strategy.asp<br>
#include "Order.h"
+
http://en.wikipedia.org/wiki/Strategy_pattern<br>
 
 
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>
 

Latest revision as of 12:34, 17 April 2007


Defenition

Allows you to define a set of algorithms, to be used by different objects. Strategy lets the algorithm vary independently from objects that use it.

Objects can be assigned to algorithms on runtime. 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.


UML Diagram

Strategy.gif

Benefits and Drawbacks of using Strategy Pattern

Benefits in using Strategy Pattern

  1. A family of algorithms can be defined as a class hierarchy and can be used interchangeably to alter application behavior without changing its architecture.
  2. By encapsulating the algorithm separately, new algorithms complying with the same interface can be easily introduced.
  3. The application can switch strategies at run-time.
  4. Strategy enables the clients to choose the required algorithm, without using a "switch" statement or a series of "if-else" statements.
  5. Data structures used for implementing the algorithm is completely encapsulated in Strategy classes. Therefore, the implementation of an algorithm can be changed without affecting the Context class.
  6. Strategy Pattern can be used instead of sub-classing the Context class. Inheritance hardwires the behavior with the Context and the behavior cannot be changed dynamically.
  7. The same Strategy object can be strategically shared between different Context objects. However, the shared Strategy object should not maintain states across invocations.

Drawbacks in using Strategy Pattern

  1. The application must be aware of all the strategies to select the right one for the right situation.
  2. Strategy and Context classes may be tightly coupled. The Context must supply the relevant data to the Strategy for implementing the algorithm and sometimes, all the data passed by the Context may not be relevant to all the Concrete Strategies.
  3. Context and the Strategy classes normally communicate through the interface specified by the abstract Strategy base class. Strategy base class must expose interface for all the required behaviors, which some concrete Strategy classes might not implement.
  4. In most cases, the application configures the Context with the required Strategy object. Therefore, the application needs to create and maintain two objects in place of one.
  5. Since, the Strategy object is created by the application in most cases; the Context has no control on lifetime of the Strategy object. However, the Context can make a local copy of the Strategy object. But, this increases the memory requirement and has a sure performance impact.

Code Sample

Strategy Method in the .NET Framework that works with ArryList.
By default the QuickSort algorithm is used to sort item in the list. Some times there is a need to use a different sort algorithm there is a overload of sort that takes an IComparer.


class CoolComparer : IComparer
{
    #region IComparer Members

    public int Compare(object x, object y)
    {
        // TODO:  implementation
        return 0;
    }

    #endregion

}

class SlowComparer: IComparer
{
    #region IComparer Members

    public int Compare(object x, object y)
    {
        // TODO:  implementation
        return 0;
    }

    #endregion

}


ArrayList items = new ArrayList();

items.Add("One");
items.Add("Two");
items.Add("Three");

items.Sort(); // Uses IComparable on string object

IComparer myComparer = new CoolComparer();
IComparer myComparer = new SlowComparer();
items.Sort(myComparer); // Delegate Comparison Method

References

http://msdn2.microsoft.com/en-us/library/system.collections.comparer(VS.80).aspx
http://www.codeproject.com/cpp/strategy.asp
http://en.wikipedia.org/wiki/Strategy_pattern