Flutter Design Patterns: 5 — Strategy

An overview of the Strategy design pattern and its implementation in Dart and Flutter

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

  • Analysis
  • Implementation
  • Other articles in this series
  • Your contribution

What is the Strategy design pattern?

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:

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

Structure of the Strategy design pattern (source)
  • 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


Implementation

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

Class Diagram — Implementation of the Strategy design pattern

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

Specific implementations of the IShippingCostsStrategy interface

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.

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.

Order

OrderItem

PackageSize

Example

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.

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.

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.

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

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.



Your contribution

Flutter Community

Articles and Stories from the Flutter Community

Mangirdas Kazlauskas

Written by

Software Engineer | Flutter Enthusiast https://www.linkedin.com/in/mangirdas-kazlauskas/

Flutter Community

Articles and Stories from the Flutter Community

More From Medium

More from Flutter Community

More from Flutter Community

More from Flutter Community

Flutter — Shadows & glows

More from Flutter Community

More from Flutter Community

How refactoring your Flutter App.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade