Home  >  

Exploring the Strategy Design Pattern

Author photo
| | Comments (4)
AddThis Social Bookmark Button

Overview


Purpose of this article

The goal of this article is to help you gain a better understanding of the Strategy design pattern. The Strategy pattern is used to separate the areas of an application that differ from the areas of an application that remain the same. This design pattern sits on top of several fundamental OOP principals. For example the Strategy pattern uses the concept of programming to an interface rather than an implementation. Strategy also favors composition over inheritance. The reason you would use the Strategy pattern is to abstract an algorithm from a class and create a new class based on the algorithm. Using polymorphism the algorithms can be changed at runtime by the compositing class. I hope you’re as excited about the topic of design patterns as I am.

Let’s go ahead and get started!

Prerequisite knowledge


OOP fundamentals

A firm handle on basic OOP fundamentals is important to understanding this article. The Strategy design pattern uses core OOP concepts. It relies on three of the four tenets of OOP.

OOP Links:

If you are not abreast in OOP basics you should take a moment to review the following material:

Object-oriented programming with ActionScript 3.0

http://www.adobe.com/devnet/actionscript/articles/oop_as3.html

Object Orientation

http://www.tricedesigns.com/tricedesigns_home/blog/2006/12/object-orientation.html

http:/www.tricedesigns.com/tricedesigns_home/blog/2006/12/object-orientation.html

Lesson: Object-Oriented Programming Concepts

http://java.sun.com/docs/books/tutorial/java/concepts/

Sample Strategy pattern examples


There are two projects that accompany this article. All of the diagrams have also been bundled and are available for download.

AS3 Strategy (no implementation)

This is a basic AS3 project that defines a very basic Strategy pattern. There is not a practical implementation for this example. It is meant to understand the key classes used in a typical Strategy pattern. (Also check out the as3-strategy-pattern-asdoc directory in this archive for more documentation.)

AS3StrategyPatternExample.zip

DataManagerClient

This example shows how three different methods for saving data can be encapsulated into objects and dynamically assigned to a compositing class. There are two runnable Flex apps in this archive.

DataManagerClient.zip

UML Diagrams

strategy-uml.zip

Strategy pattern concepts


Strategy pattern official definition:

Here is the definition of the Strategy design pattern from the Gang of Four’s book:

Define a family of algorithms, encapsulate each one, and make them interchangeable. [The] Strategy [pattern] lets the algorithm vary independently from clients that use it. - Gang of Four's (GOF) Design Patterns

This definition begins to explain things however we’ll take a deeper look into this pattern in this article. Strategy is a good pattern to learn to get started with learning design patterns. The Factory, Decorator, Composite and Iterator are great patterns to follow up Strategy with.

Core concepts and usage

Understanding the concepts behind the Strategy pattern will help reinforce your understanding of several OOP fundamentals. The Strategy pattern consists of three core OOP concepts: encapsulation, inheritance and polymorphism. This makes it a great pattern to study and learn. Once you understand this pattern you will probably begin to recognize it in other projects and codebases. The Strategy pattern can be used when the behavior of a class needs to be changed often, or changed at runtime. Strategy is useful when an inheritance chain needs to be created and you want to avoid duplicate method bodes in subclasses. Another use of the Strategy design pattern is to refactor complex conditional or branching logic.

Encapsulating algorithms

The core idea behind the Strategy pattern is the idea of encapsulating an algorithm into a class. These algorithm based classes will be concrete implementations of an algorithm interface. This enables the classes that are implementing the algorithms to dynamically change the algorithm currently being used and therefore altering the behavior that the implementing class is providing.

Pattern participants

Here is a quick overview of the main participants of the Strategy design pattern:

  • • Context superclass
    Defines the properties and methods for all Context subclasses and defines the method to change the strategy instance being used by the Context.
  • • Context subclasses
    In a typical Strategy design pattern subclasses of the Context class will be created. The method implementations will not be duplicated across the subclasses when using the Strategy design pattern. Instead the methods are moved into their own classes (or “strategies”).
  • • Algorithm interface
    The algorithm interface will define the methods available to each concrete strategy implementation.
  • • Concrete algorithm classes
    A family of interchangeable algorithm strategies.
  • • Client class
    Creates an instance of the Context class and creates an instance of an algorithm implementation. Assigns the Context instances strategy property to the algorithm instance.
Core pattern diagram

The Strategy pattern can be seen below in its purist form. This is the straight design pattern with no implementation details. This is basically the same diagram that is presented in the GoF book. It may help to review the concrete implementations of the pattern presented in this article and then return to review the core pattern.

Strategy Design Pattern
Core OOP and Strategy

Three fundamental OOP principals are being utilized in this design pattern: encapsulation, inheritance and polymorphism. You should also note that this pattern is using composition over inheritance. This is another core OOP concept.

Data Manager example


Sample Strategy pattern implementation

To gain a better understanding of a pattern it’s often a good idea to create a code example. The Data Manager example will define three different data saving “strategies” and each data saving algorithm, or strategy, will be encapsulated into a class. The data saving strategies will all implement a common data saving algorithm interface. The actual data saving algorithms would be implemented in the concrete strategy implementations.

Data Manager classes

This example is made up of the following classes:

  • • DataManagerClient
    Creates and assembles the objects used in the strategy pattern. Has the option to swap the Context classes algorithm at runtime via the setSaveDataMethod method.
  • • DataManager
    The DataManager class is the Context class which is the superclass for any subclass variations of DataManager; such as ServerDataManager.
  • • ISaveData
    This interface defines the contract that all concrete data saving strategies must follow. Remember using an interface enables polymorphism and is a key player in the Strategy design pattern.
  • • SaveDataToSharedObject
    This class contains an algorithm to save data to a local Shared Object (note: actual code for Shared Object persistence not included with this example).
  • • SaveLocalSQLiteData
    This class contains an algorithm to save data to a local SQLite database (note: actual code for SQLite persistence not included with this example). This algorithm should probably only be enabled for AIR based applications.
  • • SaveServerSideData
    This class contains an algorithm to save data to server side data persistence mechanism (note: actual code for server side persistence not included with this example).
  • • MessageLogger
    The message logging class is a very, very simple class to help display runtime information in the example.
Data saving “strategies”

Moving each algorithm into a class allows you to update the algorithm independently of any classes that use the algorithm. This is helpful when you have one superclass with multiple subclasses and you do not want to embed methods for the algorithms in each of the subclasses.

Using an interface to enable polymorphism

In the Data Manager example the ISaveData interface defines methods that all implementing classes must declare; again this is one of the key concepts of polymorphism. Classes that use concrete implementations of the ISaveData interface are able to change from one ISaveData implementation to another at runtime.

Three data saving strategies

The Data Manager example has three concrete implementations of the ISaveData interface: SaveServerSideData, SaveLocalSQLiteData and SaveDataToSharedObject. These implementations are empty in the example but this is where the data saving algorithms would go. At this point the power of the Strategy pattern should start to become clear to you. The classes that utilize these algorithms can change the data saving strategy whenever they want to.

Strategy Design Pattern
Changing the strategy

The Context class in the Data Manager example is the DataManager class. The DataManager class defines a setSaveDataMethod method. Defining this method on the class is another core piece of setting up the Strategy pattern. This will enable instances of DataManager to change the data saving method’s strategy at runtime. This is why an interface is used and also why concrete implementations of the interfaces are created.

Running the Data Manager example

When the Data Manager example is launched a simple application is presented and three radio button controls can be seen in the applications view. When one of the radio buttons is selected the client changes the algorithm the DataManager instance is using to save its data. In the example each algorithm class has a unique message that will be sent to the TextArea. The message seen in the TextArea will change when the Save button is clicked. Take a moment to run the application and test it out. We’ll look at the code for the application in more detail next.

Exploring the code DataManager Class

Let’s take a look at the source code for DataManager.as.

package com.seantheflexguy.dataManager
{
 	import mx.collections.ArrayCollection;
 	
 	public class DataManager
 	{
  		
  		private var saveDataMethod:ISaveData;
  		
  		public function DataManager()
  		{
   		}
  		
  		public function setSaveDataMethod(newMethod:ISaveData):void
  		{
   			saveDataMethod = newMethod;
   		}
  		
  		public function save(dataCollection:ArrayCollection):void
  		{
   			saveDataMethod.save(dataCollection);
   		}
  
  	}
}

This is the data management base class in the sample application. In the following example you’ll see how it is extended and the behavior for saving data is separated into its own class. Notice the setSaveDataMethod method. It accepts an argument newMethod which is an ISaveData data type. The actual type of data saving algorithm can be any existing data saving algorithm or a new algorithm. Keep in mind that the actual DataManager does not need to be modified. Sublasses also do not need to be modified to change the behavior of the data saving algorithm. The algorithms are interchangeable. The save method calls the data saving algorithms save method and passes the data that needs to be saved as a single argument.

Exploring the code ISaveData interface

The interface defined in ISaveData.as is simple but very effective.

package com.seantheflexguy.dataManager
{
 	import mx.collections.ArrayCollection;
 	
 	public interface ISaveData
 	{
  		function save(dataCollection:ArrayCollection):void;
  	}
}

Defining the save method in the ISaveData interface creates the contract for all implementing classes to follow. This enables polymorphism for concrete ISaveData implementations.

Exploring the code SaveServerSideData Class

Here is a concrete implementation of the ISaveData interface.

package com.seantheflexguy.dataManager
{
 	import mx.collections.ArrayCollection;
 	import mx.utils.ObjectUtil;
 	
 	
 	public class SaveServerSideData implements ISaveData
 	{
  		public function SaveServerSideData()
  		{
   		}
  
  		public function save(dataCollection:ArrayCollection):void
  		{
   			MessageLogger.log(">>save: SaveServerSideData - "+
   					    "save data to a remote server, "+
   					    "perhaps a database.");
   			MessageLogger.log(">>save: dataCollection: \n"+
   					    ObjectUtil.toString(dataCollection));
   			// ...
   		}
  		
  	}
}

Notice that the class implements ISaveData. The actual data persistence algorithm has been omitted from the example. (Data persistence is beyond the scope of this article so it’s not covered here.)

Exploring the code SaveLocalSQLiteData Class

Here is another concrete implementation of the ISaveData interface.

package com.seantheflexguy.dataManager
{
 	import mx.collections.ArrayCollection;
 	import mx.utils.ObjectUtil;
 	
 	public class SaveLocalSQLiteData implements ISaveData
 	{
  		public function SaveLocalSQLiteData()
  		{
   		}
  
  		public function save(dataCollection:ArrayCollection):void
  		{
   			MessageLogger.log(">>save: SaveLocalSQLiteData - "+
   					    "save data to a local SQLite database. " + 
   					    "this strategy could only be used for "+
   					    "AIR based applications.");
   			MessageLogger.log(">>save: dataCollection: \n"+
   					    ObjectUtil.toString(dataCollection));
   			// ...
   		}
  		
  	}
}

This class contains the algorithm needed to save data to a local SQLite database. This algorithm should of course only be used for AIR applications as SQLite is not available to a straight Flex application. You can begin to see the reasoning behind the Strategy pattern here though once again. One data saving algorithm/strategy is used when the application is in “distributed/online” mode and another data saving algorithm/strategy is used when the application is in “local/desktop” mode.

Exploring the code SaveDataToSharedObject Class

Here is yet another concrete implementation of the ISaveData class.

package com.seantheflexguy.dataManager
{
 	import mx.collections.ArrayCollection;
 	import mx.utils.ObjectUtil;
 	
 	public class SaveDataToSharedObject implements ISaveData
 	{
  		public function SaveDataToSharedObject()
  		{
   		}
  
  		public function save(dataCollection:ArrayCollection):void
  		{
   			MessageLogger.log(">>save: SaveDataToSharedObject - "+
   					    "save data to a local shared object.");
   			MessageLogger.log(">>save: dataCollection: \n"+
   					    ObjectUtil.toString(dataCollection));
   			// ...
   		}
  		
  	}
}

This implementation could be used in the “distributed/online” version of the application if the data need to be saved locally.

Exploring the DataManagerClient MXML file

This is a runnable Flex application that pulls the pieces of the Strategy pattern together.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundGradientAlphas="[1.0, 1.0]" 
	backgroundGradientColors="[#F5F5F5, #E9E9E9]">
	<mx:Script>
		<![CDATA[
		
			import mx.collections.ArrayCollection;
			import mx.events.ItemClickEvent;
			
			import com.seantheflexguy.dataManager.DataManager;
			import com.seantheflexguy.dataManager.ISaveData;			
			import com.seantheflexguy.dataManager.SaveDataToSharedObject;
			import com.seantheflexguy.dataManager.SaveLocalSQLiteData;
			import com.seantheflexguy.dataManager.SaveServerSideData;			
			
			private var dataManager:DataManager = new DataManager();
			private var collection:ArrayCollection = new ArrayCollection();
			
			private function radioGroupClick(event:ItemClickEvent):void
			{
 				var saveDataMethod:ISaveData = event.item as ISaveData;
 				dataManager.setSaveDataMethod(saveDataMethod);
 			}
			
			private function save():void
			{
 				collection.addItem({data:'D1', label:'data object 01'});
 				collection.addItem({data:'D2', label:'data object 02'});
 				collection.addItem({data:'D3', label:'data object 03'});
 				dataManager.save(collection);
 			}
			
		]]>
	</mx:Script>
	<mx:TextArea id="message" 
		width="414" height="204"/>
	<mx:RadioButtonGroup id="saveDataMethodRadioGroup" 
		itemClick="radioGroupClick(event);" />
	<mx:RadioButton groupName="saveDataMethodRadioGroup" 
		label="Save to server" 
		value="{new SaveServerSideData()}"/>
	<mx:RadioButton groupName="saveDataMethodRadioGroup" 
		label="Save to local SQLite Database" 
		value="{new SaveLocalSQLiteData()}"/>
	<mx:RadioButton groupName="saveDataMethodRadioGroup" 
		label="Save to local Shared Object" 
		value="{new SaveDataToSharedObject()}"/>
	<mx:Button click="save();" 
		label="Save" />
</mx:Application>

The client assembles a Strategy for saving data and provides a visual way to change the strategy being used by the DataManager instance. Launch the application to see the different logging messages written to the UI when you click the “Save” button and select from the different Radio buttons to change the save algorithm.

DataManager as a superclass

Sub classes of the DataManager class can also take advantage of the relationships defined within the Strategy pattern. The next example, Server Data Manager will demonstrate how a subclass of DataManager can be created and also use the different data saving algorithms that have already been created and defined. Also keep in mind that new strategies can added in the future.

Server Data Manager example


Creating a subclass of DataManager

In this sample scenario let’s assume a request has been issued from the project owner to create an application that has the ability to save data to a server side database and nothing else. This is where another piece of the Strategy pattern comes into focus. We can create a subclass of DataManager to fulfill the project owner’s request. The subclass will be a simplified version of DataManager and could potentially contain properties that are not needed by DataManager. There is still functionality we want from DataManager though, including the ability to swap out the save method at runtime.

Server Data Manager classes

This example is made up of the following classes:

  • ServerDataManagerClient
    Creates and assembles the objects used in the strategy pattern. This client will not change strategies to start out; it does however have the ability to do so. In fact a new server side data saving algorithm can be created and that new algorithm can be used by the ServerDataManagerClient class by calling the setSaveDataMethod method. The setSaveDataMethod method would be passed an instance of the new concrete strategy containing the new server side data saving algorithm.
  • ServerDataManager
    The ServerDataManager class is a subclass of DataManager. The ServerDataManager class relies on composition to store an instance of a concrete ISaveData implementation.
  • ISaveData
    This interface defines the contract that all concrete data saving strategies must follow. Remember using an interface enables polymorphism and is a key player in the Strategy design pattern.
  • SaveServerSideData
    This class contains an algorithm to save data to server side data persistence mechanism (note: actual code for server side persistence not included with this example).
  • MessageLogger
    The message logging class is a very, very simple class to help display runtime information in the example.
Server Data Manager client

The client implementing the Strategy design pattern will set the save method for the instance of the ServerDataManager object that it creates to a SaveServerSideData object. The client does have the ability to change the data saving strategy in the future. For now it will just use a SaveServerSideData strategy.

Strategy Design Pattern
Exploring the code ServerSideDataManager Class

This class represents a subclass of the DataManager superclass.

package com.seantheflexguy.dataManager
{
 	public class ServerDataManager extends DataManager
 	{
  		
  		public function ServerDataManager()
  		{
   			super();
   		}
  		
  		public function pingServer():void
  		{
   			MessageLogger.log(">>pingServer: ServerDataManager - "+
   					    "ping the remote server.");
   			//...
   		}		
  	}
}

You should notice that the actual code that performs the save algorithm is not in the DataManager superclass and it is not in the ServerSideDataManger subclass either. Instead the algorithm has been moved into its own class and implements a common interface enabling DataManager and ServerSideDataManager to use and change the algorithm being executed at any time they desire. Also notice that the ServerSideDataManager class defines a method specific to its needs, pingServer. It may also make sense to move the algorithm to ping the server to a class and apply a Strategy to it as well. For simplicity we’ll leave it as-is for this exercise.

Exploring the ServerSideDataManagerClient MXML file

This is a runnable Flex application that pulls the pieces of the Strategy pattern together. This application is a simplified version of DataManager that only allows data to be persisted to a remote database. Take a moment to think about how creating new strategies for saving data to different types of remote databases could be accomplished using the Strategy design pattern.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundGradientAlphas="[1.0, 1.0]" 
	backgroundGradientColors="[#F5F5F5, #E9E9E9]"
	creationComplete="init();">
	<mx:Script>
		<![CDATA[
		
			import mx.collections.ArrayCollection;
			
			import com.seantheflexguy.dataManager.ISaveData;
			import com.seantheflexguy.dataManager.ServerDataManager;
			import com.seantheflexguy.dataManager.SaveServerSideData;
			
			private var serverDataManager:ServerDataManager = new ServerDataManager();
			private var saveDataMethod:ISaveData = new SaveServerSideData();
			private var collection:ArrayCollection = new ArrayCollection();
			
			private function init():void
			{
 				serverDataManager.setSaveDataMethod(saveDataMethod);
 			}
			
			private function save():void
			{
 				collection.addItem({data:'D1',label:'data object 01'});
 				collection.addItem({data:'D2',label:'data object 02'});
 				collection.addItem({data:'D3',label:'data object 03'});
 				serverDataManager.save(collection);
 			}
		]]>
	</mx:Script>
	<mx:TextArea id="message" 
		width="414" height="204"/>
	<mx:Button click="save();" 
		label="Save" />
	<mx:Button label="Ping Server" 
		click="serverDataManager.pingServer();"/>
</mx:Application>

This application is almost identical to the Data Manager Client. Notice that there is no means to change the data saving strategy in this application though. There could be a need to implement different remote database saving algorithms later or enable this application to save to local SQLite databases in an AIR mode. The changes that an application will encounter throughout its lifetime are never ending and hard to plan for. Using design patterns can help prepare your codebase for the constant growth and change that are bound to occur. Hopefully you are starting to see some of the benefits of this particular pattern at this point in the article.

Review


Putting it all together

Looking back at everything outlined so far there are a number of key concepts to grasp and take with you. The concept of separating and encapsulating the algorithm into a class is a fundamental concept being used. “Encapsulate what varies.” There is also the use of polymorphism with the strategy interface and its concrete implementations. The Server Data Manager example also demonstrates how to cleanly separate a group of methods from a of superclass and subclass so the details of the methods are easily changeable.

Summary

I hope this article has given an insight to the power of the Strategy pattern and opened the window to using more design patterns in your application development process. Codebases that take advantage of design patterns are often easier to update and maintain and they are also more scalable to future change. Definitely take a moment to review the sample applications and review the UML diagrams as well. This article combined with the diagrams and code examples should allow you to begin using the Strategy pattern in your work.

Additional Information


Web:

Strategy pattern
Wikipedia
http://en.wikipedia.org/wiki/Strategy_pattern

Strategy UML diagram - GoF version
http://www.tml.tkk.fi/~pnr/GoF-models/html/Strategy.html

Java Design Patterns Strategy
www.fluffycat.com
http://www.fluffycat.com/Java-Design-Patterns/Strategy/

Strategy
www.dofactory.com
http://www.dofactory.com/Patterns/PatternStrategy.aspx#_self1

Books:

Design Patterns: Elements of Reusable Object-Oriented Software
Addison-Wesley Professional - Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides
http://www.informit.com/store/product.aspx?isbn=0201633612

Head First Design Patterns
O'Reilly Media, Inc.: Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates
http://oreilly.com/catalog/9780596007126/

ActionScript 3.0 Design Patterns
O'Reilly Media, Inc.: William B. Sanders, Chandima Cumaranatunge
http://oreilly.com/catalog/9780596528461/

Refactoring to Patterns
Addison-Wesley Professional: Joshua Kerievsky

Read more from Sean Moore. Sean Moore's Atom feed seantheflexguy on Twitter

Comments

4 Comments

Bill Sanders said:

Hi Sean,

That was a wonderful article on the Strategy pattern. (I made a link from our AS3 Strategy pattern article to yours from www.as3dp.com.) I'm very interested in the ArrayCollection class and the other classes available through mx.

I'm still getting used to Flex, limiting myself to ActionScript projects, and as a result I was unable to load and run your DataManager strategy pattern in Flex 3. I placed the DataManagerClient folder in the Flex Builder 3 folder with the other applications I've built, but it doesn't show up in the Flex Navigator. What am I missing?

I've got Alaric Cole's book on Flex. Have you done any or have any recommendations?

Thanks,
Bill

Sean Moore said:

Hi Bill,

Thanks for much for taking the time to check out my article. Thank you very much for the link from the as3dp site as well. I own and have read a fair amount of that title.

ArrayCollection is certainly a really awesome class provided by the Flex framework. There are so many other great classes available as well.

I would highly recommend Programming Flex 2 from O'Reilly. I read it from cover to cover and I can say with confidence that it is an awesome book for learning about Flex. I am currently reading Programming Flex 3 and so far it's equally as good. Other than that the video training on Lynda.com is great. David Gassner does an outstanding job getting you up to speed on the important features of Flex development.

The Flex projects for my article are archived Flex projects. Try importing them into Flex Builder vs. manually adding them to your project directory. Try File -> Import, then choose the Archive file option and then browse to the desired Flex archive project (.zip file). Flex archives are a great way to share projects. Let me know if you have any problems.

Thanks again for taking the time to check out my article and also providing your feedback.

Kind regards,

Sean

omair said:

Hi Sean,

This is the great article for strategy pattern. I understand each and every line you written, can you tell me some professional examples where to use it?. I truly want to use it in my professional practices.

Thanks
Omair Rais
http://www.omairarts.com

jadd said:

Hi Sea, great article. Only one question: Which software do you use for the UML diagrams showed?
Thanks, Roberto (jadd).

Leave a comment


Tag Cloud

Question of the Week: Dream App

If you had an unlimited budget and unlimited resources what application would you build and why would you build it?

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.