Thursday, December 10, 2009

Understanding Strategy Pattern and Polymorphism - Two Birds With One Stone

First off, many thanks to the people that messaged me saying they enjoyed the Observer Pattern tutorial for saving composites. I'm really glad it helped demystify patterns and OO for you! And can I add it was pretty cool that these people were a mix from both CF and C# camps :) I am a big advocate of design patterns so I will not be shutting up about them anytime soon, sorry!

Today I wanted to touch on my favorite behavioral pattern - the Strategy Pattern.

What does this pattern solve? If you have an object that:
  1. Is constantly being subclassed just to override specific implementations (methods).
  2. Has a fugly method(s) that has to run through a mine field of if/case statements just to execute.
Let me start of by first saying when you use this pattern you can truly see polymorphism at its finest. And yes, polymorphism is achievable through ColdFusion. If someone tells you different, they are smoking crack. Say no to drugs.

I won't bore you with the computer science explanation of polymorphism so here's my simple, to-the-point summation. If objects are of different types but share a common derivative(s), they should be interchangable. This should help, lets keep it simple:
If A = B and A = C then B = C.
In the example above, B and C can behave polymorphically because they both share a commonality - they both are a derivation/implementation of A. Understand this critical principle of OO and you are well on your way to your OO awakening.

Moving on. Here's the official definition of the Strategy Pattern:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
What's this mean? I've underlined the key words in this definition. When you hear algorithms, think verbs which mean methods. When you hear encapsulate, think about delegating responsibilities to new objects whose sole purpose is to protect "something" and ensure that these objects are doing only  "something" and doing it well (SRP principle). And lastly, when you hear interchangeable think polymorphism. I'll reword that definition a bit to be in line with what I just described.
Define a family of methods and make sure you've encapsulated each distinct method implementation (algorithm) away into new objects so these objects may be switched out polymorphically at run time.
First, here's an abstract class diagram I put together:

For the purposes of this tuorial just imagine that the inside of the legacy code for subject.doStrategy() you've got the if/case problem going on. It's riddled with them. 
e.g. -if something, set this then return this way, if something else set that and return that way. You get the idea. 

In the class diagram above notice:
  • We have a Subject, think of this as concrete class that has a composite relationship to an implementation of IStrategyBehavior which subject.doStrategy() delegates to. 
  • The idea is that subject.doStrategy() should NOT have any switches or ifs or overridden via subclassing just to provide concrete implementation.
  • Also notice the UML note. We delegate to another object that implements IStrategyBehavior through composition (Subject is composed of IStrategyBehavior), this is where that whole "encapsulate each one, and make them interchangeable" is happening. We're saying listen, Subject, yes you will doStrategy() however you are delegating to a "strategy" property and ask it to call its own doStrategy(). This means at runtime you, Subject, don't care about how your are doStrategy()'ing, you're handing it of to your polymorphic property "strategy" and calling doStrategy() on that. We've just encapsulated that behavior away from Subject into its own set of classes or family of algorithms.
OK now we can move on to concrete designing but first lets understand our requirements We're going to use the standard gaming example well because COD:Modern Warfare 2 just came out and it's glorious. (xbox live : Error401, add me)
  • Build a Soldier that can fight() 
  • fight() cannot have any if's/switches inside of it
  • fight() will execute differently based off what weapon Soldier is holding - Pistol, Rocket Launcher and Machine Gun
Here is the concrete design:

Here notice the following:
  • We've modeled a Soldier (Subject) and are creating a strategy pattern for subject.fight().
  • Interface ISolder has a fight() method with the same contract for for fight() in IFightBehavior. 
  • We've created three concrete implementations (concrete strategies) of IFightBehavior - PistolBehavior, MachinegunBehavior and RocketLauncherBehavior.
  • Side note : This pattern can also be achieved through an abstract class and subclassing but try to stay away from inheritance as much as you can. Favor composition and interface based programming. You have more flexibility that way.
Code time - Soldier.cfc (I've omitted showing the interfaces. It should be straight forward how to create them from the class diagram above):

  • Critical piece of code on line 2 - fightBehavior is polymorphic. Interface based programming baby. In most of my examples I will program to an interface, not an implementation (concrete type). At run time this property can be switched out by  any object that implements IFightBehavior. That's polymorphism peeps. Simple as that. Don't over complicate it.
  • fight() delegates to the fightBehavior property. In the abstract portion of this entry, when I said that a Subject, in this case Soldier, doesn't care how it does doStrategy(), in this case fight() - this is exactly what I mean. You're simply calling getFightBehavior().fight(). Soldier doesn't care how its actually doing it because it could be doing it n number of ways through polymorphism. All Soldier cares about is that fight() returns a string. And we the developer have ensured that it will since fightBehavior implements the IFightBehavior interface which also has a matching fight() method that expects a return of string.
Here is what your concrete behaviors might look like. I've consolidated it into one image in order - PistolBehavior (goes Bang!), MachineGunBehavior(lots of bangs!) and RocketLauncherBehavior(goes Boom!):

  • The classes simply implement IFightBehavior and provide a concrete implementation for fight() in their own unique way. This is their only responsibility.
  • These three classes are now interchangeable with any return type, property or method argument with the type of IFightBehavior. Like where? Like in Soldier's fightBehavior property!
And finally the implementation and the results when run:

  • While I'm hard coding the actual setFightBehavior() above, let's imagine that the setting of the weapon came from a weapon select box from a form. When the form is posted you process the selections and dynamically set the behavior and subsequently call soldier.fight().
  • If you added 50 more weapons to the select box as long as there were 50 more behavioral classes that implemented IFightBehavior, your consuming code DOES NOT CHANGE and you don't have to needlessly subclass and override methods anymore. That's the beauty and power of this pattern - polymorphism at runtime which keeps your API clean. In our example above, calls to solder.fight() need not change.

To drive it home for the last time. A solution without this pattern would most likely force you to add additional subclasses or add if/case's in solder.fight() that checks for weapon a soldier is holding, do some process logic then execute fight() in a specific fashion. Unnecessary complexity.

So there you have it - the strategy pattern in a nutshell. I've managed to work out some pretty slick solutions with this pattern in CF and C# from organizing complex search algorithms to encapsulating gnarly business logic and I'm sure you could find umpteen more uses for it. This pattern keeps complexity low, forces objects to be highly focused with singular responsibility all while being easily scalable and maintainable.

Hopefully I helped you get another step closer to object oriented design pattern nirvana. And remember, as John Whish said to me and I couldn't agree more, "Death to inheritance - long live the strategy pattern!"


p.s. - which pattern is next?


Ryan Anklam said...

Micky, these design pattern posts are excellent! I've learned a lot from reading them, keep on posting.

Micky Dionisio said...

Thanks for the feedback Ryan, glad you're learning a lot from it ;)

I certainly need to get back to posting, it's been awhile.

Are there any topics in OO, design patterns or something else that you're interested in reading about?

Ryan Anklam said...

As a matter of fact there is. I've been trying to come to grips with the anemic domain anti-pattern lately. Especially when its applied to a MVC pattern with controller and service layers.

Micky Dionisio said...

Ahhh fantastic suggestion, that'll be a fun topic to write about.

Sweet man, will put some thoughts together and get a post out there within the next couple weeks.