Easy patterns: Strategy

Ruslan Malogulko
ITNEXT
Published in
4 min readApr 3, 2019

--

Structure of the Strategy pattern

This article is created in continuation of easy patterns series description and presents a behavioral pattern named a Strategy which helps to define a family of algorithms and make them interchangeable for a client.

Please refer to the other pattern articles as well:

Creational patterns:

Simple Factory

Factory method

Builder

Singleton

Abstract factory

Prototype

Structural patterns:

Adapter

Decorator

Bridge

Composite

Facade

Flyweight

Proxy

Behavioral patterns:

Visitor

Mediator

Observer

Memento

Iterator

Chain Of Responsibility

Strategy (this article)

State

The main essence

When your class needs to include many options of specific functionality which dependent on client type (for example to process text format according to specific operation system) it’s useful to separate such functionality and let your class to have only one desired functionality which is needed in this concrete moment for this client.

Different algorithms will be appropriate at different times, we don’t need them all in our class initially. Don’t even taking into account fact that it’s hard to add some new algorithm kind if it’s an integral part of some class already. So, just a way better to keep such algorithm kinds in separate encapsulated classes.

This pattern includes two main roles:

  • Strategy—declares an interface and implementation for each supported algorithm.
  • Context — your class that maintains a reference to a Strategy object and defines an interface that lets Strategy access its data.

Generally talking about Strategy and Context, there are two ways of their communication. A Context may pass the data required by the Strategy when its specific algorithm is called. Alternatively, the Context can pass itself as an argument to the Strategy operations. Second options gives a possibility to call back on the context once the strategy algorithm is finished (for example in some async operations).

Often there is a family of Strategy classes is defined for a client to choose from.

Example of use

In this example we will create a coffee machine (Context) which accepts different Strategy objects and process a coffee drink according to the applied strategy. Each Strategy has one property with name and one method named process which contains a specific algorithm. process method returns an updated cup object according to internal logic. As you can see it’s super easy to switch between different strategies on runtime.

Profit

Families of related algorithms can help to keep Strategy classes in order and reuse them across different Context classes.

Inheritance across such family of related algorithms can help to factor out common functionality of the algorithms to some parent Strategy class and reuse it across child Strategy classes.

This pattern helps to vary the algorithm dynamically (in contrast when you subclass Context class with some nested implementation of algorithms inside).

Strategies eliminate conditional statements which means that you avoid different behaviors mix in one class, but have a separate logic according to separate Strategy object.

Client can choose among different implementation of the same behavior according to some current needs (for example optimized code for production or verbose and slow code for development needs).

Weak places

Clients must be aware of different Strategies. The potential problem that a client must understand how some specific Strategies differ before select the appropriate one. That’s why the best is to use a Strategy pattern only when the variation in behavior is relevant to clients.

This pattern can lead to a potential communication overhead between Strategy and Context. In some cases Strategy implementation can be quite simple and won’t use all the information passed to it through the common shared interface of a Strategy class. In other words the amount of information that Context tends to pass inside the Strategy algorithm can be too much than a Strategy algorithm really needs.

Also this algorithm can be an overhead if your logic has a small variation. The number of Strategy objects can hugely increase the complexity of your code and decrease its readability. So, maybe if/else statement sometimes is better though.

Conclusion

This pattern is also known as a Policy.

Developer should check that shared strategies should not maintain some common state across invocations. Context is the only place for keeping internal state and the duty of a Strategy to provide just an algorithm to solve some calculation problem.

This pattern is really good in places when you need to switch dynamically across algorithms when client time or application conditions are changing dynamically. It can improve readability of the code in cases of huge variety of algorithms used in app. But also can be useless in cases when simple logic is needed with poor variety.

If you found this article helpful, please hit the 👏 button and feel free to comment below!

--

--