Seth Barrett

Daily Blog Post: April 7th, 2023

design1

Apr 7th, 2023

Understanding the Strategy Pattern in Design Patterns
Strategy Pattern:

The Strategy Pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The pattern lets the algorithm vary independently from clients that use it.

When to use the Strategy Pattern

You should consider using the Strategy Pattern when you have a class that has multiple algorithms that can be used interchangeably, and you want to separate the implementation of the algorithms from the class that uses them.

UML Diagram

Here's the UML diagram for the Strategy Pattern:

Context <|-- ConcreteContext
    Strategy <|-- ConcreteStrategyA
    Strategy <|-- ConcreteStrategyB
    Context -* Strategy

  • Context: defines the interface for the client to interact with.
  • ConcreteContext: implements the Context interface and maintains a reference to a Strategy object.
  • Strategy: defines the interface for the algorithms.
  • ConcreteStrategyA, ConcreteStrategyB: implements the Strategy interface and provides the concrete implementation for the algorithm.

Example

Suppose we have a Sorter class that can sort an array of integers. The Sorter class has a sort method that takes an array of integers and sorts them using a particular algorithm. We want to be able to use different sorting algorithms interchangeably without modifying the Sorter class.

Here's an example implementation of the Sorter class using the Strategy Pattern:

public class Sorter {
    private SortingAlgorithm sortingAlgorithm;

    public Sorter(SortingAlgorithm sortingAlgorithm) {
        this.sortingAlgorithm = sortingAlgorithm;
    }

    public void setSortingAlgorithm(SortingAlgorithm sortingAlgorithm) {
        this.sortingAlgorithm = sortingAlgorithm;
    }

    public void sort(int[] arr) {
        sortingAlgorithm.sort(arr);
    }
}

interface SortingAlgorithm {
    void sort(int[] arr);
}

class QuickSort implements SortingAlgorithm {
    @Override
    public void sort(int[] arr) {
        System.out.println("Sorting using Quick Sort");
        // implementation of Quick Sort algorithm
    }
}

class MergeSort implements SortingAlgorithm {
    @Override
    public void sort(int[] arr) {
        System.out.println("Sorting using Merge Sort");
        // implementation of Merge Sort algorithm
    }
}

In this example, we created a Sorter object with QuickSort as the sorting algorithm and sorted an array using the sort method. We then changed the sorting algorithm to MergeSort and sorted the same array again.

We have two concrete implementations of the SortingAlgorithm interface: QuickSort and MergeSort. These classes provide the implementation for their respective sorting algorithms.

To use the Sorter class, we can create an instance of it with a specific SortingAlgorithm, and then call its sort method:

Sorter sorter = new Sorter(new QuickSort());
int[] arr = {4, 2, 5, 1, 3};
sorter.sort(arr); // Sorting using Quick Sort

sorter.setSortingAlgorithm(new MergeSort());
sorter.sort(arr); // Sorting using Merge Sort

In this example, we created a Sorter object with QuickSort as the sorting algorithm and sorted an array using the sort method. We then changed the sorting algorithm to MergeSort and sorted the same array again.

This example demonstrates how the Strategy Pattern allows us to vary the algorithm used by the Sorter class without modifying the Sorter class itself. We can easily add new sorting algorithms in the future by implementing the SortingAlgorithm interface.

This example demonstrates how the Strategy Pattern allows us to vary the algorithm used by the Sorter class without modifying the Sorter class itself. We can easily add new sorting algorithms in the future by implementing the SortingAlgorithm interface.

By using this pattern, we can reduce code duplication and ensure that each state class is responsible for handling its own behavior. This makes our code more organized, maintainable, and easier to understand.

I hope you found this post informative and helpful. In the next post, we will be discussing the Strategy pattern, which is another behavioral pattern that allows us to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. Stay tuned!

Here's the completed C# code for the State pattern example:

using System;

    // State interface
    interface IState
    {
        void Handle(Context context);
    }
    
    // Concrete State classes
    class StateA : IState
    {
        public void Handle(Context context)
        {
            Console.WriteLine("State A: Performing operation A.");
            context.SetState(new StateB());
        }
    }
    
    class StateB : IState
    {
        public void Handle(Context context)
        {
            Console.WriteLine("State B: Performing operation B.");
            context.SetState(new StateC());
        }
    }
    
    class StateC : IState
    {
        public void Handle(Context context)
        {
            Console.WriteLine("State C: Performing operation C.");
            context.SetState(new StateA());
        }
    }
    
    // Context class
    class Context
    {
        private IState _state;
    
        public Context(IState state)
        {
            _state = state;
        }
    
        public void SetState(IState state)
        {
            _state = state;
        }
    
        public void Request()
        {
            _state.Handle(this);
        }
    }
    
    // Client code
    class Client
    {
        static void Main(string[] args)
        {
            Context context = new Context(new StateA());
            context.Request();
            context.Request();
            context.Request();
        }
    }