Real World Example of the Strategy Pattern

Strategy Pattern in real life

The easiest way to do a bad job with design patterns is to learn a pattern, and then go trying to find places to put it. I did this a few times when I was first learning, and the results were quite frustrating.

The strategy pattern is a solution for a specific shape of problem. That problem is " I need to do things that are basically the same, but there are some variations in the middle". So for now, remember how to do a strategy pattern, and when you see a problem that suggests it, use it.

Polymorphism vs Strategy pattern

For me, the link from CKing post and the example in Wikipedia are clear enough, but I'll try to give you a new example. As they said, Strategy Pattern is mostly a way to change the behaviour of an algorithm at runtime. Of course you can achieve this in many different ways (such as holding a value and using switch-case, but it wouldn't be as nice as Strategy Pattern).

Let's say you're developing a turn-based strategy game with two kind of Units: Infantry and Tank (subclasses of Unit). Your terrain could be Plains, Railroad or Forests.

class Unit{
MovementStrategy ms;
final int baseMovement;
int x,y;

public Unit(int baseMovement){
this.baseMovement = baseMovement;
}

abstract void fire();

void moveForward(){
x = x + ms.getHexagonsToMove(baseMovement);
}

void setMovementStrategy(MovementStrategy ms){
this.ms = ms;
}
}

Any Unit subclass must implement fire() method because it's going to be completely different for them (Tank shots heavy long-distance round and Infantry shot several short distance light bullets). In this example we use normal polymorphism/inheritance since fire() method will be really different for any unit, and it won't change during the game.

class Infantry extends Unit{
public Infantry(){
super(2);
}

void fire(){
//whatever
}
}

class Tank extends Unit{
public Tank(){
super(5);
}

void fire(){
//whatever
}
}

Units also are able to move, and have a field baseMovement that holds the number of hexagons it can walk. We're developing a strategy game, not a real world simulation, so we don't care how they move, we just want to add a value to their coordinates (in my example I only use X coordinate in order to get a simpler code). If all the terrain was the same, we wouldn't need any Strategy object... but we need to change the behaviour of move() method at runtime!

So, we implement a different MovementStrategy class for each of our kinds of Terrain, and we program our game to trigger a setMovementStrategy() to any unit that move on each hexagon. And we don't even need to write anything else in our Unit subclasses.

interface MovementStrategy{
public int getHexagonsToMove(int base);
}

class PlainMovementStrategy implements MovementStrategy{
public int getHexagonsToMove(int base){
return base;
}
}

class RailroadMovementStrategy implements MovementStrategy{
public int getHexagonsToMove(int base){
return base*3;
}
}

class ForestMovementStrategy implements MovementStrategy{
public int getHexagonsToMove(int base){
return (int)(base/2);
}
}

Now, when any Unit move inside a Forest, we call

unit.setMovementStrategy(new ForestMovementStrategy());

And as soon it goes to a Plain, we do:

unit.setMovementStrategy(new PlainMovementStrategy());

Now we're able to change how far away our units move depending on the Terrain, and we don't need to rewrite in any of the subclasses.

I hope this helps you a better understanding of the difference.

Where is the benefit in using the Strategy Pattern?

The point is to separate algorithms into classes that can be plugged in at runtime. For instance, let's say you have an application that includes a clock. There are many different ways that you can draw a clock, but for the most part the underlying functionality is the same. So you can create a clock display interface:

class IClockDisplay
{
public:
virtual void Display( int hour, int minute, int second ) = 0;
};

Then you have your Clock class that is hooked up to a timer and updates the clock display once per second. So you would have something like:

class Clock
{
protected:
IClockDisplay* mDisplay;
int mHour;
int mMinute;
int mSecond;

public:
Clock( IClockDisplay* display )
{
mDisplay = display;
}

void Start(); // initiate the timer

void OnTimer()
{
mDisplay->Display( mHour, mMinute, mSecond );
}

void ChangeDisplay( IClockDisplay* display )
{
mDisplay = display;
}
};

Then at runtime you instantiate your clock with the proper display class. i.e. you could have ClockDisplayDigital, ClockDisplayAnalog, ClockDisplayMartian all implementing the IClockDisplay interface.

So you can later add any type of new clock display by creating a new class without having to mess with your Clock class, and without having to override methods which can be messy to maintain and debug.

Strategy Pattern with strategies contains similar code

The end goal is not to reduce as much code duplication as possible. The goal is to make the code maintainable. You need to ask yourself if reducing this duplication would make the strategy implementations easier or harder to maintain.

If reducing the duplication makes the code harder to maintain, then keep the duplication, but possibly find another way to mitigate the concern (documentation and tests are two possible ways).

If reducing the duplication would make the code easier to maintain, then think of the possible ways of removing duplication (delegation, like with Strategy and Decorator; inheritance, like with Template Method, etc).

Strategy Pattern V/S Decorator Pattern

The strategy pattern allows you to change the implementation of something used at runtime.

The decorator pattern allows you augment (or add to) existing functionality with additional functionality at run time.

The key difference is in the change vs augment

In one of the questions you linked to it also points out that with the strategy pattern the consumer is aware that the different options exist, whereas with the decorator pattern the consumer would not be aware of the additional functionality.

As an example, imagine you are writing something to sort a collection of elements. So you write an interface ISortingStrategy you can then implement several different sorting strategies BubbleSortStrategy, QuickSortStrategy, RadixSortStrategy, then your application, based on some criteria of the existing list chooses the most appropriate strategy to use to sort the list. So for example if the list has fewer than 10 items we will use RadixSortStrategy, if fewer than 10 items have been added to the list since the last sort we will use BubbleSortStrategy otherwise we will use QuickSortStrategy.

We are changing the type of sort at runtime (to be more efficient based on some extra information.) this is the strategy pattern.

Now imagine someone asks us to provide a log of how often each sorting algorithm is used to do an actual sort and to restrict sorting to admin users. We can add both of these pieces of functionality by creating a decorator which enhances any ISortingStrategy. We could create a decorator which logs that it was used to sort something and the type of the decorated sorting strategy. And we could add another decorator that checks if the current user is an administrator before calling the decorated sorting strategy.

Here we are adding new functionality to any sorting strategy using the decorator, but are not swapping out the core sorting functionality (we used the different strategies to change that)

Here is an example of how the decorators might look:

public interface ISortingStrategy
{
void Sort(IList<int> listToSort);
}

public class LoggingDecorator : ISortingStrategy
{
private ISortingStrategy decorated;
public LoggingDecorator(ISortingStrategy decorated)
{
this.decorated=decorated;
}

void Sort(IList<int> listToSort)
{
Log("sorting using the strategy: " + decorated.ToString();
decorated.Sort(listToSort);
}
}

public class AuthorisingDecorator : ISortingStrategy
{
private ISortingStrategy decorated;
public AuthorisingDecorator(ISortingStrategy decorated)
{
this.decorated=decorated;
}

void Sort(IList<int> listToSort)
{
if (CurrentUserIsAdministrator())
{
decorated.Sort(listToSort);
}
else
{
throw new UserNotAuthorizedException("Only administrators are allowed to sort");
}
}
}

What is an example in the real world that uses the Mediator pattern?

Mediator is an approach to add a third party object in order to control the interaction between a group of (2 or more) objects.

The simplest example you can find is the Chat Room example, where you can see a the a ChatRoom object controls the interaction between 2 (or more) User objects. In practice if you see a web-application like Facebook, it creates a web-socket for each of the chat-box you open. So actually the web-socket communicates with the mediator(server) and the client. When a group chat is happening each client is in synch with the server using dedicated web sockets.

Understand the Decorator Pattern with a real world example

Decorator pattern achieves a single objective of dynamically adding
responsibilities to any object.

Consider a case of a pizza shop. In the pizza shop they will sell few pizza varieties and they will also provide toppings in the menu. Now imagine a situation wherein if the pizza shop has to provide prices for each combination of pizza and topping. Even if there are four basic pizzas and 8 different toppings, the application would go crazy maintaining all these concrete combination of pizzas and toppings.

Here comes the decorator pattern.

As per the decorator pattern, you will implement toppings as decorators and pizzas will be decorated by those toppings' decorators. Practically each customer would want toppings of his desire and final bill-amount will be composed of the base pizzas and additionally ordered toppings. Each topping decorator would know about the pizzas that it is decorating and it's price. GetPrice() method of Topping object would return cumulative price of both pizza and the topping.

EDIT

Here's a code-example of explanation above.

public abstract class BasePizza
{
protected double myPrice;

public virtual double GetPrice()
{
return this.myPrice;
}
}

public abstract class ToppingsDecorator : BasePizza
{
protected BasePizza pizza;
public ToppingsDecorator(BasePizza pizzaToDecorate)
{
this.pizza = pizzaToDecorate;
}

public override double GetPrice()
{
return (this.pizza.GetPrice() + this.myPrice);
}
}

class Program
{
[STAThread]
static void Main()
{
//Client-code
Margherita pizza = new Margherita();
Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

Console.ReadLine();
}
}

public class Margherita : BasePizza
{
public Margherita()
{
this.myPrice = 6.99;
}
}

public class Gourmet : BasePizza
{
public Gourmet()
{
this.myPrice = 7.49;
}
}

public class ExtraCheeseTopping : ToppingsDecorator
{
public ExtraCheeseTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 0.99;
}
}

public class MushroomTopping : ToppingsDecorator
{
public MushroomTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}

public class JalapenoTopping : ToppingsDecorator
{
public JalapenoTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}


Related Topics



Leave a reply



Submit