Satyabrata Dash, Harish
SK and Avijeet Dash
Intent
Define a family of algorithms, encapsulate each one, and make them
interchangeable. Strategy lets the algorithm vary independently from clients
that use it.
In 30 Seconds
The catch here is "separate the invariant and variant behaviors of a
system". It enables one to break the design into two parts.
- The "context" part of the design that deals with the behavior at
an abstract level. The behavior is thus referred to as the
"strategy" for accomplishing a task. The context is the
"invariant" part of the design in that its behaviors do not
change from situation to situation. Thus it can be
encapsulated into a single object. - The "strategy" part of the design captures the
"variant" nature of the design where the particular actions are
chosen dynamically in the run time. The strategy section consists of an
abstract strategy class and a series of concrete strategy
subclasses. Each subclass represents a possible specific action
that could be taken when the strategy is executed by the
context. The variant nature of the design has been
abstracted and captured in a tree hierarchy.
Motivation
Consider the implementation scenario when the client needs to choose an
algorithm out of various complex options available.
- We can put all the business logic in the client class. The client will use
conditional statements to choose an algorithm. It will make client code
bigger and difficult to maintain when the logic is complicated and there are
many variations of them.
- The client can use subclassing of an object to implement the different
behaviour in the subclass. But it will make the context tightly coupled to
behaviour. So the object can’t dynamically change its behaviour.
- Strategy pattern offers a solution to the above problems by decoupling the
context from behaviour. It uses a Context class, which is responsible for
maintaining and finding the behaviours. The ConcreteStrategy classes implement
the algorithms that make the task to maintain code very easier. Whenever we
need to add, delete or modify the algorithm, we can do so in respective
ConcreteStrategy classes.
The strategy pattern, as the intent says, is, in short, a 'Pluggable
Algorithm'. For identifying a strategy pattern in a design in order that it can
be applied, it is necessary to first identify if an algorithm exists, and then
see whether the algorithm can be logically separated from the class, which is
intended to use it. The next step is to see whether the algorithm implementation
is expected to change later.
Let us take an example, assume a class responsible for calculating total
price of items in a shopping cart. The simplest implementation would be just
adding up the price of each item, which could be done by a simple method
calculatePrice(items); here we don't need any Strategy. Suppose there are
conditions to it like discount only for cat food only on Sundays, this involves
additional logic and just having a 'Rate of Discount' would not do. So, here we
have a 'Normal' price and 'Cat Discount' price, which is a situation that calls
for using the strategy pattern. We could then have a Strategy interface called
‘PricePolicy’, ‘NormalPricePolicy’ and ‘SundayCatPricePolicy’ would
be the concrete strategies. The Algorithm interface would be calculatePrice(
Item objects list). The default policy class could be NormalPricePolicy and
SundayCatPricePolicy be introduced only for Sundays.
Structure
Sample Code
Participants-
Context
It maintains a reference to Strategy object.
Strategy
It is an abstract class that declares the interface to all supported algorithm.
ConcreteStrategy
It implements the algorithm.
Client
It also knows about the ConcreteStrategy objects.
Example-
// Context class
class Context{ private Strategy strategy= new ConcreteStrategyA(); //Default Algorithm public void changeStrategy(Strategy newStrategy){ strategy=newStrategy; } public void getResult(){ strategy.findAlgorithm(); } } // Abstract Strategy class abstract class Strategy{ public abstract void findAlgorithm(); } // ConcreteStrategyA class class ConcreteStrategyA extends Strategy{ public void findAlgorithm(){ System.out.println("ConcreteStrategyA algorithm is used now"); } }
// ConcreteStrategyB class
class ConcreteStrategyB extends Strategy{ public void findAlgorithm(){ System.out.println("ConcreteStrategyB algorithm is used now"); } } //Client class public class Client{ public static void main(String args<>){ Context ctx=new Context(); ctx.getResult(); ctx.changeStrategy(new ConcreteStrategyB()); ctx.getResult(); } }
Complexities simplified
Client knows different Strategies
Here the Client is exposed to various Strategy implementations. The Client
must know about all the patterns before selecting one. So the Client code must
be changed to accommodate any new algorithms introduced.
Communication overhead between Strategy and Context
All ConcreteStrategy classes share the same Strategy interface. So all of
them must implement the operations defined in the Strategy class even though
they may not need it. We can have a work around this by giving a default
implementation in the abstract class and let only the required classes over-ride
that default implementation.
State and Strategy pattern
Strategy pattern seems very similar to the State pattern. But they are
different. One difference is that Strategies are determined outside the Context
(passed to it by the client) while States are more internal (not accessible
outside the Context).
Nesting strategies
It is also possible that another strategy can be used within calculatePrice()
itself, we could use another strategy to get price of each item say
ItemPricePolicy. Instead of having a getPrice() for each item, we could set an
ItemPricepolicy for each item.
Also Known as and Related Patterns
It is also known as Policy.
The related patterns are
Adapter
The Adapter pattern is structurally similar to the Strategy pattern. The
difference is in the intent. The Adapter pattern allows a client object to carry
out its originally intended function by calling methods of objects that
implement a particular interface. The Strategy pattern provides objects that
implement a particular interface for the purpose of altering or determining the
behavior of a client object.
Flyweight
If there are many client objects, they may share strategy objects if they are
implemented as Flyweights.
Template
Method
The Template method pattern manages alternate behaviors through subclassing
rather than delegation.
Reference
Using
the Strategy Design Pattern to Compose Reliable Distributed Protocols
The Strategy Pattern Lab at Oberlin
Design Patterns in the Java AWT
Example of the Strategy Design Pattern
A Memory-Constrained Image-Processing Architecture