Design patterns and practices in .NET: the Strategy Pattern

Introduction

The strategy pattern is one of the simplest patterns to implement. Have you ever written code with ugly if-else statements where you check some condition and then call another method accordingly? Even worse: have you written if-else statements to check the type of an object and then called another method depending on that? That’s not really object-oriented, right? The strategy pattern will help you clean up the mess by turning the if statements into objects – aka strategies – where the objects implement the same interface. Therefore they can be injected into another object that has a dependency of that interface and which will have no knowledge of the actual concrete type.

Starting point

Open Visual Studio and create a new blank solution. We’ll simulate a simple shipping cost calculation application where the calculation will depend on the type of the carrier: FedEx, UPS, Schenker. Add a Class Library called Domain with the following three classes:

Address.cs:

public class Address
	{
		public string ContactName { get; set; }
		public string AddressLine1 { get; set; }
		public string AddressLine2 { get; set; }
		public string AddressLine3 { get; set; }
		public string City { get; set; }
		public string Region { get; set; }
		public string Country { get; set; }

		public string PostalCode { get; set; }
	}

ShippingOptions enumeration

public enum ShippingOptions
	{
		UPS = 100,
		FedEx = 200,
		Schenker = 300,
	}

Order.cs:

public class Order
	{
		public ShippingOptions ShippingMethod { get; set; }
		public Address Destination { get; set; }
		public Address Origin { get; set; }
	}

The domain objects shouldn’t be terribly difficult to understand. The shipping cost calculation is definitely a domain logic so we’ll include the following domain service in the Domain layer:

public class ShippingCostCalculatorService
	{
		public double CalculateShippingCost(Order order)
		{
			switch (order.ShippingMethod)
			{
				case ShippingOptions.FedEx:
					return CalculateForFedEx(order);

				case ShippingOptions.UPS:
					return CalculateForUPS(order);

				case ShippingOptions.Schenker:
					return CalculateForSchenker(order);

				default:
					throw new Exception("Unknown carrier");

			}

		}

		double CalculateForSchenker(Order order)
		{
			return 3.00d;
		}

		double CalculateForUPS(Order order)
		{
			return 4.25d;
		}

		double CalculateForFedEx(Order order)
		{
			return 5.00d;
		}
	}

If you are not familiar with patterns and practices then this implementation might look perfectly normal to you. We check the type of the shipping method in an enumeration and then call on a method to calculate the cost accordingly.

What’s wrong with this code?

It is perfectly reasonable that we may introduce a new carrier in the future, say PerfectHaulage. If we pass an order with this shipping method to the CalculateShippingCost method then we’ll get an exception. We’d have to manually extend the switch statement to account for the new shipment type. In case of a new carrier we’d have to come back to this domain service and modify it accordingly. That breaks the Open/Closed principle of SOLID: a class is open for extensions but closed for modifications. In addition, if there’s a change in the implementation of one of the calculation algorithms then again we’d have to come back to this method and modify it. That’s generally not a good practice: if you make a change to one of your classes, then you should not have to go an modify other classes and public methods just to accommodate that change.

If a change in one part of the project necessitates changes in another part of the project then chances are that we’re facing some serious coupling issues between two or more classes. If you repeatedly have to revisit other parts of your software after changing some class then you probably have a design issue.

Also, the methods that calculate the costs are of course ridiculously simple in this demo – in reality there may well be calls to other services, the weight of the package may be checked etc., so the ShippingCostCalculatorService class may grow very large and difficult to maintain. The calculator class becomes bloated with logic belonging to UPS, FedEx, DHL etc, violating the Single Responsibility Principle. The service class is trying to take care of too much.

There are problems related to the design of the domain. We could argue that it is not the responsibility of the Order domain to know which shipping method a customer prefers. This is up to the domain experts to decide, but this is certainly a possible implementation problem.

However, whether or not this is the correct distribution of responsibility the switch statement may possibly reveal a flaw in our domain design. Isn’t this some domain logic that we’re hiding in the ShippingCostCalculatorService class? Shouldn’t this logic be transformed into a domain on its own? The strategy pattern will help to elevate these hidden concepts to proper objects.

Solution

The solution is basically to create a class for each calculation – we can call each implemented calculation a strategy. Each class will need to implement the same interface. If you check the calculation methods in the service class then the following interface will probably fit our needs:

public interface IShippingStrategy
	{
		double Calculate(Order order);
	}

Next we’ll create the implemented strategies:

Schenker:

public class SchenkerShippingStrategy : IShippingStrategy
	{
		public double Calculate(Order order)
		{
			return 3.00d;
		}
	}

UPS:

public class UpsShippingStrategy : IShippingStrategy
	{
		public double Calculate(Order order)
		{
			return 4.25d;
		}
	}

FedEx:

public class FedexShippingStrategy : IShippingStrategy
	{
		public double Calculate(Order order)
		{
			return 5.00d;
		}
	}

The cost calculation service is now ready to accept the strategy from the outside. The new and improved service looks as follows:

public class CostCalculationService_WithStrategy
	{
		private readonly IShippingStrategy _shippingStrategy;

		public CostCalculationService_WithStrategy(IShippingStrategy shippingStrategy)
		{
			_shippingStrategy = shippingStrategy;
		}

		public double CalculateShippingCost(Order order)
		{
			return _shippingStrategy.Calculate(order);
		}
	}

I believe you agree that this class structure is a lot more polished and follows good software engineering principles. You can now implement the IShippingStrategy as new carriers come into the picture and the calculation service can continue to function without knowing anything about the concrete strategy classes. The concrete strategy classes are self-containing, they can be tested individually and they can be mocked – more on mocking here.

Variation

This is not the only way to implement the strategy pattern. You can use the delegate approach as well.

The strategies can be written as delegates, for example:

Func<Order, double> upsStrategy = delegate(Order order) { return 4.00d; };

Then the shipping cost calculator service may look like this:

public class ShippingCostCalculatorService
    {
        public double CalculateShippingCost(Order order, Func<Order, double> shippingCostStrategy)
        {
           return shippingCostStrategy(order);
        }
    }

So we inject the delegate to the method performing the calculation rather than through a constructor injection as in the original solution.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

20 Responses to Design patterns and practices in .NET: the Strategy Pattern

  1. Gaurav J says:

    Some one still need to instantiate different shipping strategy and provide the calculator with correct strategy. Wouldn’t the same switch (or if else) shifts to that class. I completely agree with the strategy class here but was simply curious about the whole solution to this problem!

  2. Jasminder says:

    Hi Andres, nice explanation of the concept. But can you give me a clarity that had we used the Chain of responsibility pattern here, then what would have been the effects, as it pretty much sounds like the CoR could have been used here.

    • Andras Nemes says:

      Hello Jasminder,
      It seems to me that there are actually two patterns called Chain of Responsibility. The one that I wrote about here is a messaging pattern with senders and receivers exchanging information. Whereas the one you’re referring to involves specifications such as And, Not and Or where you can chain the selection criteria in a fluent way such as this:

      public bool CanBuy()
      {            
            ISpecification<Customer> canBuy = _customerAccountIsActive.And(_hasReachedThreshold.Not()).And(_customerAccountHasLateFees.Not());
            return canBuy.IsSatisfiedBy(this);             
      }
      

      This is sort of a composite specification pattern.

      It’s perfectly reasonable to transform the strategy pattern into a fluent composite to achieve the same goal. I think it’s only a matter of taste how you solve the problem in this case. Some programmers may feel uncomfortable at first with chaining the extension methods And, Not, so the strategy pattern seems more straightforward to most users.

      //Andras

  3. Jasminder says:

    Yes you are right Andras.

    My point was that whether all the methods of a Strategy pattern implementation give the same result like for ex, if we implement a sorting strategy, we can have bubble sort, quick sort etc. All of them will return a sorted list. On the other hand, if we take the example you have provided, every method is giving different result. So does it really matter that our implementations give us the same result, but the method of getting the result is different or we can have different result based implementations ?

    So if the results could be different then, we can also think of using the CoR.

    • Andras Nemes says:

      Yes, you can certainly use the Composite pattern – I think it’s wrong to call it the Chain of responsibility pattern – as described here to select a strategy, but I believe that’s not the true purpose of the pattern. The composite pattern allows you to treat objects and groups of object as a single unit. Extension methods allow you to chain the operators – which is probably the reason for the naming confusion – in order to link a range of specifications: the customer must be between 30 and 50, have at least 1 child, live in a city etc. to evaluate some condition, so we build up one single specification out of many smaller ones.

      My conclusion is that yes the Composite pattern can be used to select a strategy, but I believe it’s conceptually wrong. It’s better to encapsulate the “IsMatch” logic within each implemented strategy.
      //Andras

  4. Jasminder says:

    Hmmm…I will have to check out the Composite pattern first then, I think…!!!

  5. modenin says:

    I Andras,
    I’ve understood the main concept, but with your example you have to manually pass the right interface based on ShippingMethod with a if or case statement. Better than before but I think that using a dictionary of all strategy in the Service and apply the right one based on shipping method will improve the code. On the other side, if you use DependendyInjection I think you could have a problem.

    class OtherCalculatorSevice
    {
    private static readonly Dictionary _strategies = new Dictionary();

    public OtherCalculatorSevice()
    {
    _strategies.Add(ShippingOptions.FedEx, new FedexShippingStrategy());
    _strategies.Add(ShippingOptions.Schenker, new SchenkerShippingStrategy());
    _strategies.Add(ShippingOptions.UPS, new UpsShippingStrategy());
    }

    public double CalculateShippingCost(Order order)
    {
    return _strategies[order.ShippingMethod].Calculate(order);
    }
    }

    and the client:

    var order2= new Order { ShippingMethod = ShippingOptions.UPS };
    if (order2…… == UPS) // Manually choose
    var StrategyService = new CostCalculationService_WithStrategy(new SchenkerShippingStrategy());
    Console.WriteLine(StrategyService.CalculateShippingCost(order2));

    //Always work without if / case
    var order3= new Order {ShippingMethod = ShippingOptions.UPS};
    var otherStrategy = new OtherCalculatorSevice();
    Console.WriteLine(otherStrategy.CalculateShippingCost(order3));

    My need is avoid case/if statement. What do you think about ?

    Ivano

    • Andras Nemes says:

      Hi Ivano,

      Check out this post where the correct strategy is selected using an IsMatch property. The solution is based on a list of concrete strategies as you’ve suggested.

      You can eliminate that list completely using reflection. You’ll find an example in the post on the Factory design pattern. However, reflection is not very efficient, so use it carefully.

      If-else cases can be acceptable in choosing the right strategy however: you cannot be sure which strategy will be selected before some other external class provides some input, e.g. a customer selecting a strategy in a drop down list. Normally, this scenario calls for an abstract factory, which is discussed in the post I mentioned above. You hide the concrete factory behind an abstract one. The concrete factory can either choose the strategy using an if-else statement or the IsMatch property from a list of implementing strategies.

      //Andras
      PS: you seem to have posted the same question twice so I removed one of them.

      • modenin says:

        Hi Andras,
        thank you for the clarification. I agree with you.
        I’ve started reading your blog from the posts about DDD and are very very usefull.
        Now I think is time to read all your posts first to ask any other question.

        You’ve done a great work, really.
        Ivano.

  6. Pingback: Design Patterns: Strategy, Observer, and Decorator | Rodan Sotto's Tech Blog

  7. Chas Chapman says:

    Hi Andras, this is great information in a very clear format. Thanks a million for posting this and for responding to questions!

  8. Pingback: SOLID design principles in .NET: the Open-Closed Principle | Michael's Excerpts

  9. Pingback: Architecture and patterns | Michael's Excerpts

  10. Vlad Vizitiu says:

    Hey Andras,

    An approach I used for this is as follow:

    I had a tool to process different kinds of files and each one of those had their own strategy. Inside that strategy indeed there was a validation (and if statement) to check that the right file was passed in and if not, do nothing.

    That way, my “processor” received a collection of strategies for parsing (which could be configurable as to what strategies get added to the collection via the config file) and just looped through all of the provided strategies. this would be a null and strategy pattern combo. We indeed use an if statement (we were bound to use one eventually in the call to the method) but it was contained inside the strategy for its own needs.

    The example shows could be considered very similar to the adapter pattern you presented because we’re using only one concrete instance of an abstraction, whereas with collections might emphasize the potential of having more than one strategy being imported.

    Thanks,
    Vlad V.

  11. Khaled says:

    What I like about your style is that you always provide a real-life problem first and then the solution. It makes a world of difference to me. I am planning to read everything you’ve written. Thank you very much!

  12. Pingback: Strategy Design Pattern |

  13. Abdiwahab Farah says:

    Hello Andras,

    Kindly show the Consumer part of the above strategy Pattern.

    Thanks

  14. Petar Jaredic says:

    Hello Andras,
    just to say that for sure Sweden has one very good software developer!
    Regards from Serbia,
    Petar.

Leave a Reply to Andras Nemes Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: