Strategy

From CDOT Wiki
Revision as of 15:21, 22 January 2007 by Rfainsht (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

In computer programming, the strategy pattern is a particular software design pattern, whereby algorithms can be selected on-the-fly at runtime.

In some programming languages, such as those without polymorphism, the issues addressed by this pattern are handled through forms of reflection, such as the native function pointer or function 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

+-------------------+               +---------------------+
|      Context      | (Containment) |   <<Interface>>     |
|-------------------|<>-------------|      Strategy       |
|-------------------|               |---------------------|
|+ContextInterface()|               |---------------------|
+-------------------+        +----|>|+AlgorithmInterface()|<|---+
                             |      +---------------------+     | 
                             |                                  |
                             |                                  |
                 (Implements)|                      (Implements)|
                             |                                  |
                  +---------------------+   +---------------------+
                  | ConcreteStrategyA   |   | ConcreteStrategyB   |
                  |---------------------|   |---------------------|
                  |---------------------|   |---------------------|
                  |+AlgorithmInterface()|   |+AlgorithmInterface()|
                  +---------------------+   +---------------------+

Code Examples

C#

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


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

#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];
}

Sorter.h

Base class for the the two sorting algorithms: QuickSort and CombSort.

#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

Sorter.cpp

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

BubbleSort.h

#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

BubbleSort.cpp

#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] );
    }
  }
}

CombSort.h

#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 

CombSort.cpp

#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;
}

Order.h

#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

Order.cpp

#include "Order.h"

void Order::Swap(string& a, string& b)
{
  string tmp = a;
  a = b;
  b = tmp;
}


Order::Order()
{}

Ascending.h

#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

Ascending.cpp

#include "Ascending.h"

bool Ascending::OutOfOrder(const string& a, const string& b) const
{
  return a > b;
}

Descending.h

#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

Descending.cpp

#include "Descending.h"

bool Descending::OutOfOrder(const string& a, const string& b) const
{
  return a < b;
}