Strategy ( eng. Strategy ) - behavioral design pattern designed to define a family of algorithms , encapsulate each of them and ensure their interchangeability. This allows you to select an algorithm by defining the appropriate class. The Strategy pattern allows you to change the selected algorithm regardless of the client objects that use it.
Strategy | |
---|---|
Strategy | |
Type of | behavioral |
Purpose | allows you to use different business rules or algorithms depending on the context. |
Used in cases | when in the same place, depending on the current state of the system (or its environment), different algorithms should be used |
pros |
|
Minuses | creation of additional classes |
Related Templates | Bridge , Template Method , Adapter |
Described in Design Patterns | Yes |
Content
Key Features
Task
According to the type of client (or the type of data being processed), select the appropriate algorithm that should be applied. If a rule that is not subject to change is used, there is no need to refer to the “strategy” pattern.
Motives
- The program should provide different variants of the algorithm or behavior.
- You need to change the behavior of each instance of the class.
- It is necessary to change the behavior of objects at runtime.
- The introduction of the interface allows the client classes to know nothing about the classes that implement this interface and encapsulate specific algorithms.
Solution Method
Separation of the algorithm selection procedure from its implementation. This allows selection based on context.
Members
- The
Strategy
class defines how the various algorithms will be used. - ConcreteStrategy concrete classes implement these various algorithms.
- The
Context
class uses concreteConcreteStrategy
classes by reference to the concrete type of the abstractStrategy
class . TheStrategy
andContext
classes interact to implement the selected algorithm (in some cases, theStrategy
class needs to send requests to theContext
class). TheContext
class forwards theStrategy
class a request from its client class.
Consequences
- The Strategy pattern defines a family of algorithms.
- This eliminates the use of switches and / or conditional statements.
- All algorithms should be called in the standard way (they should all have the same interface).
Implementation
The class that uses the algorithm ( Context
) includes the abstract class ( Strategy
), which has an abstract method that determines how the algorithm is invoked. Each derived class implements one required variant of the algorithm.
Note: the method of invoking the algorithm should not be abstract if you want to implement some default behavior.
Useful Information
- both the strategy and the decorator can be used to change the behavior of specific classes. The advantage of the strategy is that the customization interface does not coincide with the public interface and may be much more convenient, and the disadvantage is that in order to use the strategy it is necessary to initially design a class with the ability to register strategies.
Use
The Microsoft WDF architecture is based on this pattern. Each object “driver” and “device” has an unchangeable part sewn into the system in which the changeable part (strategy) is registered, written in a specific implementation. The variable part can be completely empty, which will give a driver who doesn’t do anything, but can also participate in PnP and power management.
The ATL library contains a set of threading model classes that are strategies (various implementations of Lock / Unlock, which are then used by the main classes of the system). At the same time, these strategies use static polymorphism through the template parameter, and not dynamic polymorphism through virtual methods.
Examples
Java example
// A class that implements a specific strategy should implement this interface
// The context class uses this interface to invoke a specific strategy.
interface Strategy {
int execute ( int a , int b );
}
// Implement the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {
public int execute ( int a , int b ) {
System . out . println ( "Called ConcreteStrategyAdd's execute ()" );
return a + b ; // Do an addition with a and b
}
}
class ConcreteStrategySubtract implements Strategy {
public int execute ( int a , int b ) {
System . out . println ( "Called ConcreteStrategySubtract's execute ()" );
return a - b ; // Do a subtraction with a and b
}
}
class ConcreteStrategyMultiply implements Strategy {
public int execute ( int a , int b ) {
System . out . println ( "Called ConcreteStrategyMultiply's execute ()" );
return a * b ; // Do a multiplication with a and b
}
}
// Context class using strategy interface
class Context {
private Strategy strategy ;
// Constructor
public Context () {
}
// Set new strategy
public void setStrategy ( Strategy strategy ) {
this . strategy = strategy ;
}
public int executeStrategy ( int a , int b ) {
return strategy . execute ( a , b );
}
}
// Test application
class StrategyExample {
public static void main ( String [] args ) {
Context context = new Context ();
context . setStrategy ( new ConcreteStrategyAdd ());
int resultA = context . executeStrategy ( 3 , 4 );
context . setStrategy ( new ConcreteStrategySubtract ());
int resultB = context . executeStrategy ( 3 , 4 );
context . setStrategy ( new ConcreteStrategyMultiply ());
int resultC = context . executeStrategy ( 3 , 4 );
System . out . println ( "Result A:" + resultA );
System . out . println ( "Result B:" + resultB );
System . out . println ( "Result C:" + resultC );
}
}
C ++ Example
#include <iostream>
class Strategy
{
public :
virtual ~ Strategy () {}
virtual void use () = 0 ;
};
class Strategy_1 : public Strategy
{
public :
void use () {
std :: cout << "Strategy_1" << std :: endl ;
}
};
class Strategy_2 : public Strategy
{
public :
void use () {
std :: cout << "Strategy_2" << std :: endl ;
}
};
class Strategy_3 : public Strategy
{
public :
void use () {
std :: cout << "Strategy_3" << std :: endl ;
}
};
class Context
{
protected :
Strategy * operation ;
public :
virtual ~ Context () {}
virtual void useStrategy () = 0 ;
virtual void setStrategy ( Strategy * v ) = 0 ;
};
class Client : public Context
{
public :
void useStrategy ()
{
operation -> use ();
}
void setStrategy ( Strategy * o )
{
operation = o ;
}
};
int main ( int / * argc * / , char * / * argv * / [])
{
Client customClient ;
Strategy_1 str1 ;
Strategy_2 str2 ;
Strategy_3 str3 ;
customClient . setStrategy ( & str1 );
customClient . useStrategy ();
customClient . setStrategy ( & str2 );
customClient . useStrategy ();
customClient . setStrategy ( & str3 );
customClient . useStrategy ();
return 0 ;
}
#include <iostream>
struct Strategy_1
{
void use () { std :: cout << "Strategy_1" << std :: endl ; };
};
struct Strategy_2
{
void use () { std :: cout << "Strategy_2" << std :: endl ; };
};
struct Strategy_3
{
void use () { std :: cout << "Strategy_3" << std :: endl ; };
};
template < class Operation >
struct Client : public Operation
{
void useStrategy ()
{
this -> use ();
}
};
int main ( int / * argc * / , char * / * argv * / [])
{
Client < Strategy_1 > customClient1 ;
customClient1 . useStrategy ();
Client < Strategy_2 > customClient2 ;
customClient2 . useStrategy ();
Client < Strategy_3 > customClient3 ;
customClient3 . useStrategy ();
return 0 ;
}
C # example
using System ;
namespace DesignPatterns.Behavioral.Strategy
{
// A class that implements a specific strategy should inherit this interface
// The context class uses this interface to invoke a specific strategy.
public interface IStrategy
{
void Algorithm ();
}
// First concrete implementation-strategy.
public class ConcreteStrategy1 : IStrategy
{
public void Algorithm ()
{
Console . WriteLine ( "The algorithm of strategy 1 is executed." );
}
}
// Second concrete implementation-strategy.
// Implementations can be as many as you like.
public class ConcreteStrategy2 : IStrategy
{
public void Algorithm ()
{
Console . WriteLine ( "Strategy algorithm 2 is running." );
}
}
// The context that uses the strategy to solve its problem.
public class Context
{
// Link to IStrategy interface
// allows you to automatically switch between specific implementations
// (in other words, it is the choice of a specific strategy).
private IStrategy _strategy ;
// Context constructor.
// Initializes the object with a strategy.
public Context ( IStrategy strategy )
{
_strategy = strategy ;
}
// Method to set the strategy.
// Used to change strategy at runtime.
// In C #, it can also be implemented as a write property.
public void SetStrategy ( IStrategy strategy )
{
_strategy = strategy ;
}
// Some context functionality that selects
// strategy and uses it to solve its problem.
public void ExecuteOperation ()
{
_strategy . Algorithm ();
}
}
// Application class.
// In this example, it acts as a context client.
public static class Program
{
// <summary>
// Entry point to the program.
// </ summary>
public static void Main ()
{
// Create a context and initialize it with the first strategy.
Context context = new Context ( new ConcreteStrategy1 ());
// Perform a context operation that uses the first strategy.
context . ExecuteOperation ();
// Replace in the context of the first strategy of the second.
context . SetStrategy ( new ConcreteStrategy2 ());
// Perform a context operation, which now uses the second strategy.
context . ExecuteOperation ();
}
}
}
D examples
import std . stdio ;
interface IStrategy
{
int Action ( int a , int b );
}
class TAddition : IStrategy
{
public int Action ( int a , int b )
{
return a + b ;
}
}
class TSubtraction : IStrategy
{
public int Action ( int a , int b )
{
return a - b ;
}
}
class TContexet
{
private :
int a , b ;
IStrategy strategy ;
public :
void SetAB ( int a , int b )
{
TContexet . a = a ;
TContexet . b = b ;
};
void SetStrategy ( IStrategy strategy )
{
TContexet . strategy = strategy ;
}
int Action ()
{
return strategy . Action ( a , b );
}
}
void main ()
{
TContexet context = new TContexet ;
context . SetAB ( 10 , 5 );
context . SetStrategy ( new TAddition );
writeln ( context . Action ()); // 15
context . SetStrategy ( new TSubtraction );
writeln ( context . Action ()); // five
}
Delphi example
program Strategy_pattern ;
{$ APPTYPE CONSOLE}
type
IStrategy = interface
[ '{6105F24C-E5B2-47E5-BE03-835A894DEB42}' ]
procedure Algorithm ;
end ;
TConcreteStrategy1 = class ( TInterfacedObject , IStrategy )
public
procedure Algorithm ;
end ;
procedure TConcreteStrategy1 . Algorithm ;
begin
Writeln ( 'TConcreteStrategy1.Algorithm' ) ;
end ;
type
TConcreteStrategy2 = class ( TInterfacedObject , IStrategy )
public
procedure Algorithm ;
end ;
procedure TConcreteStrategy2 . Algorithm ;
begin
Writeln ( 'TConcreteStrategy2.Algorithm' ) ;
end ;
type
TContext = class
private
FStrategy : IStrategy ;
public
procedure ContextMethod ;
property Strategy : IStrategy read FStrategy write FStrategy ;
end ;
procedure TContext . ContextMethod ;
begin
FStrategy . Algorithm ;
end ;
var
Context : TContext ;
begin
Context : = TContext . Create ;
try
Context . Strategy : = TConcreteStrategy1 . Create ;
Context . ContextMethod ;
Context . Strategy : = TConcreteStrategy2 . Create ;
Context . ContextMethod ;
finally
Context . Free ;
end ;
end .
Javascript Examples
// "interface" Strategy
function Strategy () {
this . exec = function () {};
};
// Strategy implementation
// show the message in the status bar of the browser
// (not supported by all browsers)
function StrategyWindowStatus () {
this . exec = function ( message ) {
window . status = message ;
};
};
StrategyWindowStatus . prototype = new Strategy ();
StrategyWindowStatus . prototype . constructor = StrategyWindowStatus ;
// show the message using popup
// (can be blocked by the browser)
function StrategyNewWindow () {
this . exec = function ( message ) {
var win = window . open ( "" , "_blank" );
win . document . write ( "<html>" + message + "</ html>" );
};
};
StrategyNewWindow . prototype = new Strategy ();
StrategyNewWindow . prototype . constructor = StrategyNewWindow ;
// show message using modal window
function StrategyAlert () {
this . exec = function ( message ) {
alert ( message );
};
};
StrategyAlert . prototype = new Strategy ();
StrategyAlert . prototype . constructor = StrategyAlert ;
// Context
function Context ( strategy ) {
this . exec = function ( message ) {
strategy . exec ( message );
};
}
// Use
var showInWindowStatus = new Context ( new StrategyWindowStatus () );
var showInNewWindow = new Context ( new StrategyNewWindow () );
var showInAlert = new Context ( new StrategyAlert () );
showInWindowStatus . exec ( "message" );
showInNewWindow . exec ( "message" );
showInAlert . exec ( "message" );
PHP examples
<? php
interface NamingStrategy
{
function createName ( $ filename );
}
class ZipFileNamingStrategy implements NamingStrategy
{
function createName ( $ filename )
{
return "http://downloads.foo.bar/ { $ filename } .zip" ;
}
}
class TarGzFileNamingStrategy implements NamingStrategy
{
function createName ( $ filename )
{
return "http://downloads.foo.bar/ { $ filename } .tar.gz" ;
}
}
class Context
{
private $ namingStrategy ;
function __construct ( NamingStrategy $ strategy )
{
$ this -> namingStrategy = $ strategy ;
}
function execute ()
{
$ url [] = $ this -> namingStrategy -> createName ( "Calc101" );
$ url [] = $ this -> namingStrategy -> createName ( "Stat2000" );
return $ url ;
}
}
if ( strstr ( $ _SERVER [ "HTTP_USER_AGENT" ], "Win" ))
$ context = new Context ( new ZipFileNamingStrategy ());
else
$ context = new Context ( new TarGzFileNamingStrategy ());
$ context -> execute ();
?>
Python 2.7 Example
class People ( object ):
tool = None
def __init__ ( self , name ):
self . name = name
def setTool ( self , tool ):
self . tool = tool
def write ( self , text ):
self . tool . write ( self . name , text )
class ToolBase :
"" "
Family of algorithms `Writing tool`
"" "
def write ( self , name , text ):
raise NotImplementedError ()
class PenTool ( ToolBase ):
"""A pen"""
def write ( self , name , text ):
print u ' % s (pen) % s ' % ( name , text )
class BrushTool ( ToolBase ):
"""Brush"""
def write ( self , name , text ):
print u ' % s (brush) % s ' % ( name , text )
class Student ( People ):
"""Student"""
tool = PenTool ()
class Painter ( People ):
"""Painter"""
tool = BrushTool ()
maxim = Student ( u 'Maxim' )
maxim write ( u 'I am writing a lecture on the Strategy pattern' )
# Maxim (pen) I am writing a lecture on the Strategy pattern
sasha = Painter ( u 'Sasha' )
sasha . write ( u 'Drawing an illustration to the Strategy pattern' )
# Sasha (brush) I draw an illustration to the Strategy pattern
# Sasha decided to become a student
sasha . setTool ( PenTool ())
sasha . write ( u 'No, I'd rather write a summary' )
# Sasha (pen) No, I'd rather write a summary
Ruby example
require "interface"
Strategy = interface {
required_methods : use
}
class StrategyOne
def use
puts "Strategy one"
end
implements Strategy
end
class StrategyTwo
def use
puts "Strategy two"
end
implements Strategy
end
class StrategyThree
def use
puts "Strategy three"
end
implements Strategy
end
class Context
attr_accessor : strategy
def initialize strategy
@strategy = strategy
end
def useStrategy
strategy . use
end
end
context = Context . new StrategyOne . new
context . useStrategy
context . strategy = StrategyTwo . new
context . useStrategy
context . strategy = StrategyThree . new
context . useStrategy
Sources of information
- E. Gamma, R. Helm, R. Johnson, J. Vlissides . Object-oriented design techniques. Design Patterns = Design Patterns: Elements of Reusable Object-Oriented Software. - SPb: “Peter” , 2007. - p. 366. - ISBN 978-5-469-01136-1 . (also ISBN 5-272-00355-1 )
- Shalloway , Alan, Trott , James, R. Design Patterns. A new approach to object-oriented analysis and design : Trans. from English —M .: Williams Publishing House, 2002. —288 p. ISBN 5-8459-0301-7
- Grand, M. Design Patterns in Java: Trans. from English .. - M .: New Knowledge, 2004. - P. 559. - ISBN 5-94735-047-5 .