Strategy Pattern

Published: by Creative Commons Licence

Definition

The Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and allows them to be interchangeable within that family. This flexibility enables the selection of an algorithm at runtime based on current needs.

Structure

classDiagram
    class Context {
        -Strategy strategy
        +setStrategy(Strategy s)
        +executeStrategy()
    }

    class Strategy {
        <<interface>>
        +algorithmInterface()
    }

    class ConcreteStrategyA {
        +algorithmInterface()
    }

    class ConcreteStrategyB {
        +algorithmInterface()
    }
    
    class ConcreteStrategyC {
        +algorithmInterface()
    }

    Context o-- Strategy : uses
    Strategy <|-- ConcreteStrategyA : implements
    Strategy <|-- ConcreteStrategyB : implements
    Strategy <|-- ConcreteStrategyC : implements

Participants

  • Strategy: Declares a common interface for all algorithms.
  • ConcreteStrategy: Implements specific algorithms defined by the Strategy interface.
  • Context: Maintains a reference to a Strategy and delegates the execution of the algorithms.

Characteristics

The Context class is used by clients to hold a reference to the Strategy. Algorithms can dynamically be interchanged by switching the ConcreteStrategy assigned to the Strategy.

Example

Consider the "SimUDuck" example from the "Head First Design Patterns" book. Ducks exhibit both common and unique behaviors. By utilizing the Strategy Pattern, different flying and quacking behaviors can be assigned without altering the Duck class.

Implementation of the SimUDuck in C#

classDiagram
	class Duck {
		<<abstract>>
		-IFlyBehavior flyBehavior
		-IQuakBehavior quackBehavior
        +setFlyBehavior()
		+setQuaukBehavior()
		+swim()
		+display()
		+performQuack()
		+performFly()
	}
	
	class MallardDuck {
		+display()
	}
	
	class RedheadDuck {
		+display()
	}
	
	class RubberDuck {
		+display()
	}
	
	class DecoyDuck {
		+display()
	}

    class ModelDuck {
		+display()
	}
	
	class IFlyBehavior {
		<<interface>>
		+fly()
	}
	
	class FlyWithWings {
		+fly()
	}
	class FlyNoWay {
		+fly()
	}

    class FlyRocketPowered {
        +fly()
    }
	
	class IQuackBehavior {
		<<interface>>
		+quack()
	}
	
	class QuackBehavior {
		+quack()
	}
	
	class SqueakBehavior {
		+quack()
	}
	
	class MuteQuackBehavior {
		+quack()
	}

    class FakeQuackBehavior {
        +quack()
    }
	
	Duck o-- IFlyBehavior : uses
	Duck o-- IQuackBehavior : uses
	
	
	Duck <|-- MallardDuck : implements
	Duck <|-- RedheadDuck : implements
	Duck <|-- RubberDuck : implements
	Duck <|-- DecoyDuck : implements
    Duck <|-- ModelDuck : implements
	
	IFlyBehavior <|-- FlyWithWings : implements
	IFlyBehavior <|-- FlyNoWay : implements
    IFlyBehavior <|-- FlyRocketPowered : implements
	
	IQuackBehavior <|-- QuackBehavior : implements
	IQuackBehavior <|-- SqueakBehavior : implements
	IQuackBehavior <|-- MuteQuackBehavior : implements
    IQuackBehavior <|-- FakeQuackBehavior : implements
  • Strategy: IFlyBehavior, IQuackBehavior
  • ConcreteStrategy: FlyWithWings, FlyNoWay, FlyRocketPowered, QuackBehavior, SqueakBehavior, MuteQuackBehavior, FakeQuackBehavior
  • Context: Duck

In this setup, different ducks can have behaviors assigned dynamically based on their characteristics. For example, Mallard and Redhead Ducks can fly and quack, therefore they use FlyWithWings and QuackBehavior. Rubber Ducks, which cannot fly but can make a squeaking sound, are therefore assigned FlyNoWay and SqueakBehavior. Decoy Ducks neither fly nor make sounds, hence they are assigned FlyNoWay and MuteQuackBehavior. Model Ducks, unable to fly but capable of quacking, are assigned FlyNoWay and QuackBehavior. The use of the Strategy Pattern allows for these behaviors to be changed without altering the Duck class itself.

Code Overview

The source code below in C# illustrates the implementation of the Strategy Pattern.

Each duck type inherits from the abstract Duck class and can exhibit various flying and quacking behaviors determined by interfaces IFlyBehavior and IQuackBehavior. The Duck class delegates flying and quacking actions to objects that implement these interfaces, which can be swapped dynamically.

// Base Duck class (Context) encapsulating behavior interfaces
public abstract class Duck(IFlyBehavior flyBehavior, IQuackBehavior quackBehavior)
{
    // Each duck has a different display method
    public abstract void Display();
    
    // Dynamically change flying behavior
    public void SetFlyBehavior(IFlyBehavior fb) => flyBehavior = fb;
    
    // Dynamically change quacking behavior
    public void SetQuackBehavior(IQuackBehavior qb) => quackBehavior = qb;

    // Delegate the behavior
    public void PerformFly()
    {
        flyBehavior.Fly();
    }
    
    // Delegate the behavior
    public void PerformQuack()
    {
        quackBehavior.Quack();
    }
    
    public void Swim() => Console.WriteLine("All ducks float, even decoys!");
}

public class MallardDuck() : Duck(new FlyWithWings(), new QuackBehavior())
{
    public override void Display() => Console.WriteLine("I'm a real Mallard duck");
}

public class RedheadDuck() : Duck(new FlyWithWings(), new QuackBehavior())
{
    public override void Display() => Console.WriteLine("I'm a real Red Headed duck");
}

public class RubberDuck() : Duck(new FlyNoWay(), new SqueakBehavior())
{
    public override void Display() => Console.WriteLine("I'm a rubber duck");
}

public class DecoyDuck() : Duck(new FlyNoWay(), new MuteQuackBehavior())
{
    public override void Display() => Console.WriteLine("I'm a decoy duck");
}

public class ModelDuck() : Duck(new FlyNoWay(), new QuackBehavior())
{
    public override void Display() => Console.WriteLine("I'm a model duck");
}

// flying behavior interface
public interface IFlyBehavior
{
    void Fly();
}

// quacking behavior interface
public interface IQuackBehavior
{
    void Quack();
}

Different behaviors are implemented by concrete classes that conform to IFlyBehavior and IQuackBehavior. This allows changing a duck's behavior at runtime by injecting different behavior instances.

// Concrete flying behaviors
public class FlyWithWings : IFlyBehavior
{
    public void Fly() => Console.WriteLine("I'm flying");
}

public class FlyNoWay : IFlyBehavior
{
    public void Fly() => Console.WriteLine("I cannot fly");
}

public class FlyRocketPowered : IFlyBehavior
{
    public void Fly() => Console.WriteLine("I'm flying with a rocket!");
}

// Concrete quacking behaviors
public class QuackBehavior : IQuackBehavior
{
    public void Quack() => Console.WriteLine("Quack");
}

public class SqueakBehavior : IQuackBehavior
{
    public void Quack() => Console.WriteLine("Squeak");
}

public class MuteQuackBehavior : IQuackBehavior
{
    public void Quack() => Console.WriteLine("<<Silence>>");
}

public class FakeQuackBehavior : IQuackBehavior
{
    public void Quack() => Console.WriteLine("Qwak");
}

// Client
Duck mallardDuck = new MallardDuck();
mallardDuck.Display();
mallardDuck.PerformQuack();
mallardDuck.PerformFly();
/*
I'm a real Mallard duck
I'm flying
Quack
*/

Duck modelDuck = new ModelDuck();
modelDuck.Display();
modelDuck.PerformFly();
modelDuck.PerformQuack();
/*
I'm a model duck
I cannot fly
Quack
*/

Duck decoyDuck = new DecoyDuck();
decoyDuck.Display();
decoyDuck.PerformFly();
decoyDuck.PerformQuack();
/*
I'm a decoy duck
I cannot fly
<<Silence>>
*/


Duck rubberDuck = new RubberDuck();
rubberDuck.Display();
rubberDuck.PerformFly();
rubberDuck.PerformQuack();
/*
I'm a rubber duck
I cannot fly
Squeak

*/

Duck duck = new MallardDuck();
duck.Display();
duck.PerformFly();
duck.PerformQuack();
/*
I'm a real Mallard duck
I'm flying
Quack
*/

// Changing behavior dynamically
duck.SetFlyBehavior(new FlyRocketPowered());
duck.PerformFly();
/*
I'm flying with a rocket!
*/

duck.SetQuackBehavior(new FakeQuackBehavior());
duck.PerformQuack();
/*
Qwak
*/

This example demonstrates how the Strategy Pattern allows for flexible and interchangeable behaviors within objects without modifying the objects themselves. By separating behavior implementation from the client classes, we enhance modularity and extendibility in software design, adhering to the open/closed principle.

Application

ViewResolver in Spring MVC

The Strategy Pattern is effectively utilized in the ViewResolver component of Spring MVC, which facilitates dynamic view resolution based on request parameters. This setup allows Spring MVC to support various rendering technologies seamlessly and adaptively.

Implementation in Spring MVC

The DispatcherServlet serves as the context in this pattern, managing an array of ViewResolver strategies. Each ViewResolver is designed to resolve view names into actual views, based on the incoming request details such as view name and locale.

Here's a simplified class diagram that illustrates the structure:

classDiagram
    class DispatcherServlet {
        -viewResolvers
    }

    class ViewResolver {
        +resolveViewName()
    }

    class AbstractCachingViewResolver {
        +resolveViewName()
    }
 
    ViewResolver o-- DispatcherServlet: uses
    ViewResolver <|-- AbstractCachingViewResolver : implements

    AbstractCachingViewResolver <|-- UrlBasedViewResolver : implements
    AbstractCachingViewResolver <|-- ThymeleafViewResolver : implements

    UrlBasedViewResolver <|-- InternalResourceViewResolver : implements

    UrlBasedViewResolver <|-- AbstractTemplateViewResolver : implements
    AbstractTemplateViewResolver <|-- FreeMarkerViewResolver: implements
  • Context: DispatcherSerlvet
  • Strategy: ViewResolver
  • Concrete Strategy: InternalResourceViewResolver, FreeMarkerViewResolver, ThymeleafViewResolver, …

This structure allows the DispatcherServlet to map requests to appropriate ViewResolvers, such as InternalResourceViewResolver, FreeMarkerViewResolver, and ThymeleafViewResolver. Each implements the resolveViewName method differently to cater to specific needs.

Code Snippet from Spring Framework

Below is a Java code snippet demonstrating the dynamic strategy selection in action within the DispatcherServlet:

public interface ViewResolver {
	@Nullable
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

public class DispatcherServlet extends FrameworkServlet {
    // Array of ViewResolver strategies
    @Nullable
    private List<ViewResolver> viewResolvers;
    
    // Resolves a view based on the view name and locale
    @Nullable
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
	    Locale locale, HttpServletRequest request) throws Exception {

        if (this.viewResolvers != null) {
		    for (ViewResolver viewResolver : this.viewResolvers) {
			    View view = viewResolver.resolveViewName(viewName, locale);
			    if (view != null) {
				    return view;
			    }
		    }
	    }
	    return null;
    }
    //...
}

In this implementation, the DispatcherServlet checks each registered ViewResolver to find a suitable view that matches the request criteria. If a valid view is found, it is returned and rendered. This demonstrates the Strategy Pattern's principle of separating the decision-making process from the execution, thus enabling high modularity and easy interchangeability of strategies.