5 - Strategy
Tags: flutter, dart, oop, programming
Published: 2021-02-15 09:41:11

In the last article, I have represented the Composite design pattern. This time, I would like to analyse and implement a design pattern which belongs to the category of behavioural design patterns — Strategy.

## Table of Contents

* What is the Strategy design pattern?
* Analysis
* Implementation
* Your Contribution

## What is the Strategy design pattern?

![Cat Reading Military Strategy](https://dev-to-uploads.s3.amazonaws.com/i/evuyf2ch7z2wkgw9c1zg.gif)

**Strategy**, also known as policy, belongs to the category of **behavioural** design patterns. The intention of this design pattern is described in the [GoF book](https://en.wikipedia.org/wiki/Design_Patterns):

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

The Strategy is considered as one of the most practical design patterns, you can find a lot of uses for it in day-to-day coding. The main idea of this pattern is to extract related algorithms (or any piece of code) into separate classes and define a common interface for them. This enables compile-time flexibility — new algorithms can be added by defining new classes, existing ones can be changed independently. Also, the extracted strategy class can be changed in the code dynamically at run-time. Another advantage of the pattern is that allows you isolate the code, internal data, and dependencies of various algorithms from the rest of the code — clients use a simple interface to execute the algorithms and switch them at run-time. Possible use-cases of the Strategy design pattern:

* Sorting algorithms — each algorithm (e.g. bubble sort, quicksort, etc.) is extracted into a separate class, a common interface is defined which provides a method *sort()*;
* Payment strategies — you want to define different payment options in your code (mobile payment, bank transfer, cash, credit card, you name it) and use them based on the user’s selection;
* Damage calculation in RPG game — there are several different types of attack in the game e.g. attacking with different moves, combos, spells, using weapons, etc. Several different algorithms could be defined for each attack type and the damage value could be calculated based on the context.

Let’s jump right in by analysing the Strategy design pattern and its implementation in more detail!

## Analysis

In the picture below you can see a general structure of the Strategy design pattern:

![Strategy Class Diagram](https://dev-to-uploads.s3.amazonaws.com/i/looqmg75fdqklmes6f2b.png)

* *Strategy* — declares an interface which is common to all supported algorithms. It also declares a method the *Context* uses to execute a specific strategy;
* *ConcreteStrategies* — implement different algorithms using the *Strategy* interface which is used by the *Context*;
* *Context* — maintains a reference to a Strategy object, but is independent of how the algorithm is implemented;
* *Client* — creates a specific strategy object and passes it to the *Context*.

### Applicability

The primary purpose of the Strategy design pattern is to encapsulate a family of algorithms (related algorithms) such that they could be callable through a common interface, hence being interchangeable based on the specific situation. Also, this pattern should be considered when you want to use different calculation logic within an object and/or be able to switch between different algorithms at run-time. A general rule of thumb — if you notice different behaviours lumped into a single class, or there are several conditional statements in the code for selecting a specific algorithm based on some kind of context or business rules (multiple if/else blocks, switch statements), this is a big indicator that you should use the Strategy design pattern and encapsulate the calculation logic in separate classes (strategies). This idea promotes the **Open-Closed Principle** (the letter **O** in [SOLID](https://en.wikipedia.org/wiki/SOLID) principles) since extending your code with new behaviour (algorithm) does not insist you to change the logic inside a single class, but allows creating a new strategy class instead — *software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification*.

## Implementation

![Alright, Let's Do It](https://dev-to-uploads.s3.amazonaws.com/i/vpt0gwmp9wl0jv5pbat9.gif)

The following example and the problem we want to resolve could look similar for some of you, which are using Flutter to build an e-commerce mobile application. Let’s say that your e-shop business offers several different shipping types for your customers:

* Picking up the ordered items from a physical store (or any other physical place, e.g. a warehouse);
* Sending order items using parcel terminal services;
* Sending order items directly to your customers in the shortest delivery time possible — priority shipping.

These three types contain different shipping costs calculation logic which should be determined at run-time, e.g. when the customer selects a specific shipping option in UI. At first sight, the most obvious solution (since we do not know which shipping option would be selected) is to define these algorithms in a single class and execute a specific calculation logic based on the customer’s choice. However, this implementation is not flexible, for instance, if you want to add a new shipping type in the future, you should adjust the class by implementing the new algorithm, at the same time adding more conditional statements in the code — this violates the Open-Closed principle, since you need to change the existing code for the upcoming business requirements. A better approach to this problem is to extract each algorithm into a separate class and define a common interface which will be used to inject the specific shipping costs calculation strategy into your code at run-time. Well, the Strategy design pattern is an obvious choice for this problem, isn’t it?

### Class diagram

The class diagram below shows the implementation of the Strategy design pattern:

![Strategy Implementation Class Diagram](https://dev-to-uploads.s3.amazonaws.com/i/qg90j2ohcx38n550agz5.png)

*IShippingCostsStrategy* defines a common interface for all the specific strategies:

* *label* — a text label of the strategy which is used in UI;
* *calculate()* — method to calculate shipping costs for the order. It uses information from the Order class object passed as a parameter.

*InStorePickupStrategy*, *ParcelTerminalShippingStrategy* and *PriorityShippingStrategy* are concrete implementations of the *IShippingCostsStrategy* interface. Each of the strategies provides a specific algorithm for the shipping costs calculation and defines it in the *calculate()* method.

*StrategyExample* widget stores all different shipping costs calculation strategies in the *shippingCostsStrategyList* variable.

### IShippingCostsStrategy

An interface which defines methods and properties to be implemented by all supported algorithms. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

![ishipping_costs_strategy.dart](https://dev-to-uploads.s3.amazonaws.com/i/6g6fbxa60o7pa8d42m8k.png)

### Specific implementations of the IShippingCostsStrategy interface

*InStorePickupStrategy* implements the shipping strategy which requires the customer to pick-up the order in the store. Hence, there are no shipping costs and the *calculate()* method returns 0.

![in_store_pickup_strategy.dart](https://dev-to-uploads.s3.amazonaws.com/i/ck0kplze25zm0gd3s0ed.png)

*ParcelTerminalShippingStrategy* implements the shipping strategy when order is delivered using the parcel terminal service. When using parcel terminals, each order item is sent separately and the shipping cost depends on the parcel size. The final shipping price is calculated by adding up the separate shipping cost of each order item.

![parcel_terminal_shipping_strategy.dart](https://dev-to-uploads.s3.amazonaws.com/i/rnsyu51uhrwixieakh6k.png)

*PriorityShippingStrategy* implements the shipping strategy which has a fixed shipping cost for a single order. In this case, the *calculate()* method returns a specific price of 9.99.

![priority_shipping_strategy.dart](https://dev-to-uploads.s3.amazonaws.com/i/tvjl52umkp3zxgq3pqex.png)

### Order

A simple class to store an order’s information. *Order* class contains a list of order items, provides a method to add a new *OrderItem* to the order, also defines a getter method *price* which returns the total price of the order (without shipping).

![order.dart](https://dev-to-uploads.s3.amazonaws.com/i/d60yqi23vdfl6h407kuo.png)

### OrderItem

A simple class to store information of a single order item. *OrderItem* class contains properties to store order item’s title, price and the package (parcel) size. Also, the class exposes a named constructor *OrderItem.random()* which allows creating/generating an *OrderItem* with random property values.

![order_item.dart](https://dev-to-uploads.s3.amazonaws.com/i/rh04pyts56obs3ee18vl.png)

### PackageSize

A special kind of class — *enumeration* — to define different package size of the order item.

![package_size.dart](https://dev-to-uploads.s3.amazonaws.com/i/t1ndqnkk78y6v455rvsq.png)

### Example

First of all, a markdown file is prepared and provided as a pattern’s description:

![Strategy Markdown](https://dev-to-uploads.s3.amazonaws.com/i/pfo2miqswizfdthm265e.gif)

*StrategyExample* implements the example widget of the Strategy design pattern. It contains a list of different shipping strategies (*shippingCostsStrategyList*) and provides it to the *ShippingOptions* widget where the index of a specific strategy is selected by triggering the *setSelectedStrategyIndex()* method. Then, the selected strategy is injected into the *OrderSummary* widget where the final price of the order is calculated.

![strategy_example.dart](https://dev-to-uploads.s3.amazonaws.com/i/d7l94hkpfpgiy08bjry8.png)

*ShippingOptions* widget handles the selection of a specific shipping strategy. The widget provides a radio button list item for each strategy in the shippingOptions list. After selecting a specific shipping strategy, the *onChanged()* method is triggered and the selected index is passed to the parent widget (*StrategyExample*). This implementation allows us to change the specific shipping costs calculation strategy at run-time.

![shipping_options.dart](https://dev-to-uploads.s3.amazonaws.com/i/mhkakxba3bfu0thrd0aw.png)

*OrderSummary* widget uses the injected shipping strategy of type *IShippingCostsStrategy* for the final order’s price calculation. The widget only cares about the type of a shipping strategy, but not its specific implementation. Hence, we can provide different shipping costs calculation strategies of type *IShippingCostsStrategy* without making any changes to the UI.

![order_summary.dart](https://dev-to-uploads.s3.amazonaws.com/i/0tco6lod1nzepmrifa8o.png)

The final result of the Strategy design pattern’s implementation looks like this:

![Strategy Example](https://dev-to-uploads.s3.amazonaws.com/i/z9piujifiwazt9rl8ufo.gif)

As you can see in the example, the shipping costs calculation strategy could be changed at run-time and the total order price is recalculated.

All of the code changes for the Strategy design pattern and its example implementation could be found [here](https://github.com/mkobuolys/flutter-design-patterns/pull/6).

## Your Contribution

💖 or 🦄 this article to show your support and motivate me to write better!
💬 Leave a response to this article by providing your insights, comments or wishes for the next topic.
📢 Share this article with your friends, colleagues in social media.
➕ Follow me on dev.to or any other social media platform.
⭐ Star the [Github](https://github.com/mkobuolys/flutter-design-patterns) repository.