Do you Know The Strategy Design Pattern?

What is Strategy Design Pattern?

Do you use Strategies?

We all have dealt with strategies in certain moments of our life. In more technical words strategy is a set of planned actions designed to achieve a specific goal. Simply, you try out many strategies when a final exam is close by. You try to collect photo copied notes from friends and study all of them within a week, you try to go to any knowledge transferring discussions for missed lessons, you try to watch some videos on YouTube and learn the subject and many more creative strategies when the exams are getting closer. From one method or another, you were able to get good marks to pass the exam. Likewise, strategy design pattern provides multiple solutions for a recurring problem in the software world.

If you are not sure about design patterns please read Introduction to Design Patterns.

Objects are behaving

As a norm in software design and development world, isolated components are called as objects. To build a successful system we need to have many objects with the ability to communicate and co-work with each other. But in most complex systems objects’ interactions cause many issues. Behavioral design patterns provide solutions to this object communication and interaction problems.

Define Strategy Pattern

Strategy design pattern is identified as a behavioral design pattern since it directly controls the object behaviors. This pattern is used to handle the software algorithms, manage interactions and responsibilities among objects to achieve a specific task. This is the more formal definition from the GOF team,

Strategy pattern defines a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

How Strategy Pattern Works

This pattern is most suitable when we have multiple solutions to one problem. In a more technical way there are multiple algorithms to achieve a specific task. Each algorithm is independent of one another and enclosed with own implantation. But all of those algorithms is capable of resolving that specific problem likewise anyone can replace one algorithm from another depending on their requirement. Due to that feature, each algorithm is independent of the client who uses it. Algorithm content and implementation are decoupled from the client who uses it and the client can select any upon runtime based on individual parameters and conditions. Below figure describes the overall mechanism of the strategy pattern.

What is Algorithm in Strategy Pattern?

The algorithm is an individual strategy. In a context of strategy design pattern, normally there is a template strategy as an abstract strategy in a common interface. Each algorithm should implement that common interface which is enforced by an abstract strategy. Each individual strategy should have a unique functionality that can be implemented as a concrete strategy.

When to use the Strategy Pattern?

When there are multiple solutions or ‘n’ number of algorithms for a certain problem and we need all of those to be available so that it is possible to make a choice at runtime

When you have different variants of one algorithm to achieve any function, each variant can enclose in a strategy

When there are multiple related classes which only differs from their behaviors, strategy pattern can be used to dynamically select the required behavior

When the objects should have easy interaction without getting tightly coupled with dependencies

When there are more private internal data to hide from outside

If you need to avoid heavy conditional statements those each behavior can be encapsulated in a strategy

List Class use Strategy Pattern?

Yes. You can see from the below diagram how the List class has used the strategy pattern. Let’s assume that you want to sort some numbers and you decide to use List class to achieve this.

List class uses the strategy pattern via SortingStrategy interface. There are 3 candidate sorting classes as Bubble Sort, Selection Sort and Insertion Sort which contain different sorting mechanisms.

Bubble Sort: Repeatedly compare neighbour pairs and swap if necessary

Selection Sort: Repeatedly pick the smallest element to append to the result

Insertion Sort: Repeatedly add new element to the sorted result

All 3 sorting algorithms will implement the Sorting Strategy interface. The sorting algorithm to use with our List class will be decided at the runtime depending on the requirement and conditions dynamically based on client’s selection.

Take the implementation of the collection. If the list has a sorting algorithm built into it – you cannot change the sorting algorithm. But when strategy pattern is injected, the sorting algorithm can be used independently while keeping the List implementation stable.

Real World Example

Strategy pattern usage can be seen in many real world scenarios as well. Let’s take an item purchasing at a shopping mall.

Basic Steps to Perform

Customer comes to the cashier System accepts customer details System calculates bill amount System apply discount based on day of the week (This is where the strategy pattern comes into the picture) Monday – 10 % Friday – 50 % System output the total bill for the customer

System has to select which discount option assign to the bill based on the day of the week

Problem – System has to apply discounts to bills

Solution – There are 3 options for discounts based on days – system has to select one and calculate the bill amount

High-Level Class Diagram for the Shopping Mall Scenario

Above figure contains the code samples for the main interface and implementing concrete classes. The rest of the class implementations are described later.

Context – includes client or decision logic to select the correct discount based on the day

LowDiscountStrategy.java

public class LowDiscountStrategy implements DiscountStrategy { @override public int getFinalBill(int amount){ return int (billAmount – (billAmount*0.1)); } } 1 2 3 4 5 6 public class LowDiscountStrategy implements DiscountStrategy { @override public int getFinalBill ( int amount ) { return int ( billAmount – ( billAmount * 0.1 ) ) ; } }

HighDiscountStrategy.java

public class HighDiscountStrategy implements DiscountStrategy { @override public int getFinalBill(int amount){ return int (billAmount – (billAmount*0.5)); } } 1 2 3 4 5 6 public class HighDiscountStrategy implements DiscountStrategy { @override public int getFinalBill ( int amount ) { return int ( billAmount – ( billAmount * 0.5 ) ) ; } }

Context Class

This class bridges a client with the strategy keeping a reference to the DiscountStrategy object. Hence, the ShoppingMallContext class will be initialized with a DiscountStrategy object in order to forward the client’s request to the strategy.

ShoppingMallContext.java

public class ShoppingMallContext { private DiscountStrategy strategy; public void setDiscountStrategy(DiscountStrategy strategy) { this.strategy = strategy; } public DiscountStrategy getDiscountStrategy() { return strategy; } public int getFinalBill(int amount) { return strategy.getFinalBill(int amount); } 1 2 3 4 5 6 7 8 9 10 11 12 public class ShoppingMallContext { private DiscountStrategy strategy ; public void setDiscountStrategy ( DiscountStrategy strategy ) { this . strategy = strategy ; } public DiscountStrategy getDiscountStrategy ( ) { return strategy ; } public int getFinalBill ( int amount ) { return strategy . getFinalBill ( int amount ) ; }

Client Class

This class is the main user of the functionality or the particular algorithm. It requests the exact strategy based on certain requirements and conditions. Client class should first invoke the Context class in order to grab the decided strategy at runtime.

ShoppingMallClient.java

public class ShoppingMallClient { public static void main(String[] args) { ShoppingMallContext context = new ShoppingMallContext(); int billAmount; // Monday context.setDiscountStrategy(new LowDiscountStrategy); billAmount = context.getFinalBill(billAmount); // Friday context.setDiscountStrategy(new HighDiscountStrategy); billAmount = context.getFinalBill(billAmount); System.out.println (“Total Bill Amount = ” + billAmount); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ShoppingMallClient { public static void main ( String [ ] args ) { ShoppingMallContext context = new ShoppingMallContext ( ) ; int billAmount ; // Monday context . setDiscountStrategy ( new LowDiscountStrategy ) ; billAmount = context . getFinalBill ( billAmount ) ; // Friday context . setDiscountStrategy ( new HighDiscountStrategy ) ; billAmount = context . getFinalBill ( billAmount ) ; System . out . println ( “ Total Bill Amount = ” + billAmount ) ; } }

Advantages of Strategy Pattern

This gives a greater flexibility and reuse

It is possible to eliminate large conditional statements using this pattern

Since this is dynamic we don’t need to find alternatives to painful hard coding

Easier to maintain the different behaviors since they are encapsulated in separate classes

Everyone needs not be aware of internal algorithms when adding new features

There are wide variety of selections for same behavior

Challenges of Strategy Pattern

This will increase the number of objects in the system which will lead to performance issues later

There will be additional operational cost due to interaction between strategies and their context

There might be confusions when implementing if the developer is not aware of all possible behaviors of strategies

Strategy pattern gives you the ability to stay independent and provide space for all possible approaches to come into the play when designing a software system. It is a great way to enhance the versatile nature of a system.

Source Code:

You can find the source code used in this tutorial on github : https://github.com/t-tak/javagists/tree/master/java-design-patterns/strategy-design-pattern

Share this: Facebook

LinkedIn

Twitter

Tumblr

Pinterest



Like this: Like Loading...