Strategies Eliminate Conditional Statements
If this entry evokes a sense déjà vu, you’d be half right. It is not another jibe to condemn conditional statements, but instead it is a discovery of another pattern where you can eliminate them. The consequences listed by Gamma, et al for the Strategy pattern includes,
Strategies eliminate conditional statements.
While I am quite familiar with the fact that the State design pattern eschews conditional statements, I was unaware that the Strategy pattern has the same feature. Rather, like everyone else I focused on the dictum, encapsulate the concept that varies when dealing with the Strategy pattern. Here I do not plan to re-hash what we covered in Chapter 11 of our book, but instead I want to look at a simple Strategy design pattern where no conditional statements are used. Also, I want to walk through a thought process when working on a simple application and show how flexible it is because of the Strategy design pattern.
It All Began with a simple MP3 Player
Creating an MP3 player in Flash is something I’ve done often and never gave it a second thought using the NetConnection and NetStream classes. However, recently I was working on one using the Sound and SoundChannel classes. Turning it on and off was trivial, but I learned that unlike the NetStream class, the SoundChannel class has no pause method. (NetStream has both toggle and non-toggle versions of pause methods.) The SoundChannel has a position property that returns the position of the MP3 file currently playing. By passing the position value to a variable and passing it to a play() method as a parameter, the play would resume where it left off. So building a player using either NetStream or SoundChannel is not rocket science.
However, suppose you decide to have a structure that could use either the NetStream or SoundChannel class. To that you might also add whether you wanted the MP3 to play on the NetStream as a progressive download or as streaming audio through Flash Media Server 3. All you have to change is the content of the algorithms to accommodate any of the three versions you envision. Of course you don’t want to dismantle all of the other work if you decide to change the program. You just need to change the algorithms.
The Abstract Architecture of a Strategy MP3 Player
When you get right down to it, you only need four algorithms:
- Start Play
- Stop Play
- Pause Play
- Unpause Play
Depending on what you have set up in the Client class for a UI and the types of classes you’re using to play the MP3 files, the details of the algorithms will vary, and you may even have a class to organize the Sound/SoundChannel or NetConnection/NetStream methods. For now, though, all I want is architecture to handle starting and stopping play and pause. This will also help reveal the structure of the Strategy design pattern. Figure 1 shows the Strategy pattern that is used in this example:

Figure 1: MP3 Player Strategy Design Pattern
The IPlayer interface is the Strategy portion of the pattern. All of the algorithms go into implementations of IPlayer, and so all we need will be four concrete strategies—one for each of the actions on the MP3 player. For this example, I’ll just use trace() statements to act as stand-ins for actual algorithms.
Context Class
The left part of the class diagram in Figure 1 is the Context participant in the pattern. As shown in the diagram, the Context class holds a reference to the Strategy (IPlayer) participant. That reference to the IPlayer can be seen in the following listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 | package { public class Context { protected var player:IPlayer; public function doControl():void { player.control(); } } } |
The reference is in typing the player instance as an IPlayer and then holding a reference to the control() method in the player. The control() method is an algorithm developed in the strategy implementations. The Context class has no idea what that reference to the player method will be but serves to make it available to a client and subclasses that inherit the Context class. In this case, four Context subclasses are created, one for each of the operations—playing, stopping play, starting pause, and turning pause off. The following listings show all of the classes that extend the Context class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | //Starter.as package { public class Starter extends Context { public function Starter() { player= new StartPlay(); } } } //Stopper.as package { public class Stopper extends Context { public function Stopper() { player= new StopPlay(); } } } //Pauser.as package { public class Pauser extends Context { public function Pauser() { player= new StartPause(); } } } //UnPauser.as package { public class UnPauser extends Context { public function UnPauser() { player= new StopPause(); } } } |
Each of the subclasses of Context instantiates a single strategy implementation, and no reference is made to the strategy’s class key method. As a result, the design pattern demonstrates a high degree of loose coupling.
The Strategy Interface and Implementations
The right side of the Strategy class diagram (Figure 1) shows the interface and implementations of the Strategy interface (IPlayer). Each of the algorithms is developed in the concrete strategies to be accessed by a client through the Context class. The algorithms can be as simple or complex as required by the goals of the application. In this particular case, the “algorithms” are nothing more than trace statements that are placeholders for whatever algorithm one wishes to use. For the practical programmer this means that when change occurs, all you need to do is to change the algorithm without worrying about conflict with other parts of the program. The following shows the Strategy interface (IPlayer) and its four concrete classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | //IPlayer.as package { //Strategy interface public interface IPlayer { function control():void; } } //StartPlay.as package { //Concrete Strategy public class StartPlay implements IPlayer { //var playMP3:PlayTune public function control():void { trace("Play tune"); } } } // StopPlay.as package { //Concrete Strategy public class StopPlay implements IPlayer { //var playMP3:PlayTune public function control():void { trace("Stop play"); } } } //StartPause.as package { //Concrete Strategy public class StartPause implements IPlayer { //var playMP3:PlayTune public function control():void { trace("Start pause"); } } } // StopPause.as package { //Concrete Strategy public class StopPause implements IPlayer { //var playMP3:PlayTune public function control():void { trace("Stop pause"); } } } |
As you can see, each of the concrete classes has a commented line with a reference to:
playMP3:PlayTune
This line represents some reference to a class that handles either the Sound/SoundChannel or NetConnection/NetStream classes.
The Client
The Gang of Four note that [a] context forwards requests from its clients to its strategy, and that [clients] usually create and pass a ConcreteStrategy object to the context. This is exactly what we have done with the Client class. As you can see in the Client.as listing below all references are to the Context class or one of its extensions where the specific algorithm from the Strategy classes are referenced via the concrete context class and not the strategy implementation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //Client.as package { import flash.display.Sprite; public class Client extends Sprite { private var musicMaster1:Context; private var musicMaster2:Context; private var musicMaster3:Context; private var musicMaster4:Context; public function Client() { musicMaster1=new Starter(); musicMaster1.doControl(); musicMaster2=new Stopper(); musicMaster2.doControl(); musicMaster3=new Pauser(); musicMaster3.doControl(); musicMaster4=new UnPauser(); musicMaster4.doControl(); } } } |
The doControl() method from the Context class is a reference to the IPlayer’s sole method, control(). However, as you can see the Client accesses the Strategy through the Context enabling a loose but effective connection between the client and the algorithms in the program.
When you test the Client, you will see each of the “algorithm’s” results.
Play tune
Stop play
Start pause
Stop pause
Each of the instantiations of the different Context classes could just as well be functions fired by a button or some other UI. Also, note that not a single conditional statement can be found in the entire program. A selection from the Client assumes a certain response with no ifs, ands or buts. As a result, there’s no conditional linkage to get tangled up in if you decide to make a change.
Do-It-Yourself MP3 Player
Now that you have the basic architecture for an MP3 player, we’d like to see if any of you can actually put something together that will play/pause/stop/unpause an actual MP3 application using this pattern. If you’re new to Design Patterns, it’ll be a chance to see if you can use them to accomplish a task beyond the abstract traces done in this example. If you’re an old hand with Design Patterns, it’s a little toy problem to solve like a crossword puzzle. The challenge is to do it in the design pattern provided. You can use either Flex or Flash (the example was developed in Flex), and the Client class should have all of the UIs and instances you’d normally have. You can add classes to support the client and you need to change all of the algorithms in the Strategy concrete classes, but it must maintain the basic class structure shown.
If you’re interested in the Strategy patterns in ActionScript 3.0, here’s an article by Sean Moore that I think you’ll like:http://www.insideria.com/2008/11/exploring-the-strategy-design.html

Great tutorials. I have been trying to do your challenge however I have come stuck with the instances of playtune. It plays alright, but when it comes to stopping, the sound channel is null. The reason I think this is happening is because I am creating a new PlayTune class for every state (play, pause, stop) and therefore disregarding the previously initiated attempt.
How will I go about sending the references from the PlayTune to and from the specified states?
Hope I was clear, and thanks for the articles and help.
Hi Bob,
You have hit the nail on the head and identified the essence of dealing with design patterns and Flash and Flex. Namely it’s not easy and how do you deal with what amounts to either a global variable (which none of us want) or to put the whole thing into a single class (easy but not a design pattern)?
Take a look at how it was done using a State pattern in Chapter 10 or another type of use in Chapter 11 where the algorithms were used with a Flash design using the Strategy pattern. I believe it forces us to look ever more closely at the nature of relations between classes in design patterns.
I’m not being coy. Rather, I’ve been busy and haven’t had time to solve the problem I presented. The idea emerged from the example in Chapter 9 using the Template Method where it was possible to switch between a video and sound player. Once the first one was solved, the other was quite easy. In this case, you have to deal with the Channel class to start and stop the MP3 play, but how does one access the same instance to start and stop the play — or set up a pause? Hours of fun!
Cheers,
Bill
Hi Bill,
Thanks for your quick and helpful reply.
I have implemented the said state pattern and got it working with sound channels.
The way I did is was by sending a reference to the playTune context class everytime the play was invoked.
Is that the best way to do it?
Thanks,
Bob
Hi Bob,
Glad it worked well for you. It sounds (no pun intended) that you did it just right.
Bill
what about a looping sound that you can pause…
Hi Sebben,
Wouldn’t that be re-starting after the first iteration? Pause would work just like a regular pause.
Let me know if I’m missing something…
Thanks,
Bill