Contents
Solution architect: "Do you have any progress?"
Dumb developer: "Yes, I think I learned how to apply the Observer pattern to solve all problems"
Solution architect: "A single pattern to solve all problems?"
Dumb developer: "Huh, isn't that enough?"
Introduction
Introduction to This Article
This is the second article in this series. Before reading this article, you should read and understand the first article in this series, titled: Design Your Soccer Engine, and Learn How To Apply Design Patterns (Observer, Decorator, Strategy, and Builder Patterns) - Parts I and II.
In my first article (see http://amazedsaint.blogspot.com, which constitutes Parts I and II), we discussed:
- What are patterns, and how to use them
- How to identify scenarios to apply patterns
- How to apply the Observer pattern to solve a design problem in our soccer engine
This article is a continuation of the previous article, and in this article, we will discuss:
- Part III: Applying the Strategy pattern to solve design problems related with 'Team' and 'TeamStrategy'
- Part IV: Applying the Decorator pattern to solve design problems related with the 'Player'
If you cannot remember these design problems, kindly go back to the first article, refer them, and then come back.
Using the Code
- The related zip file includes the code, UML designs (in Visio format) etc., for demonstrating the application of Strategy and Decorator patterns. After reading this article, you may download and extract the zip file - using a program like WinZip - to play with the source code.
Part III
Applying the Strategy Pattern
In this section, we will have a closer look at the Strategy pattern, and then we will apply the pattern to solve our second design problem. Refer to the previous article at this point - just to remind yourself of our second design problem.
If you can remember, our second design problem was:
- Specific design problem: When the game is in progress, the end user can change the strategy of his team (e.g., from Attack to Defend).
- Problem Generalized: We need to let the algorithm (TeamStrategy) vary independently from clients (in this case, the Team) that use it.
As we discussed earlier, when the game is in progress, we need to change the strategy of the team (e.g., from Attack to Defend). This clearly means that we need to separate the Team's strategy from the Team that uses it.
As we know, we can apply the Strategy pattern to solve the above design problem, because it lets the algorithm (i.e., the Team's strategy) to vary independently from the clients (i.e., the Team) that use it. Let us see how we can apply the Strategy pattern to solve this design problem.
Understanding the Strategy Pattern
The Strategy pattern is pretty simple. The UML diagram for the Strategy Pattern is shown below:
Fig. - Strategy Pattern
The participants of the pattern are detailed below:
Strategy
This class is an abstract class for the algorithm (or strategy), from which all concrete algorithms are derived. In short, it provides an interface common to all the concrete algorithms (or concrete strategies). I.e., if there is an abstract (must override) function called foo()
in the Strategy
class, all concrete strategy classes should override the foo()
function.
ConcreteStrategy
This class is where we actually implement our algorithm. In other words, it is the concrete implementation of the Strategy
class. Just for an example, if Sort
is the strategy class which implements the algorithm, then the concrete strategies can be MergeSort
, QuickSort
, etc.
Context
Context
can be configured with one or more concrete strategies. It will access the concrete strategy object through the strategy interface.
Adapting the Strategy Pattern
Now, let us see how we actually adapt the Strategy pattern to solve our problem. This will give you a very clear picture.
Fig. - Solving Our Second Design Problem
Here, the TeamStrategy
class holds the Play
function. AttackStrategy
and DefendStrategy
are the concrete implementations of the TeamStrategy
class. Team
holds a strategy, and this strategy can be changed according to the situation of the match (for example, we change the active strategy from AttackStrategy
to DefendStrategy
if we are leading by a number of goals - huh, well, I'm not a good football coach anyway). When we call the PlayGame
function in Team
, it calls the Play
function of the current strategy. Have a look at the code. It is straightforward, and everything is commented neatly.
By using the Strategy pattern, we separated the algorithm (i.e., the strategy of the team) from the Team
class.
Strategy Pattern Implementation
TeamStrategy (Strategy)
The code for the TeamStrategy
class is shown below:
Public MustInherit Class TeamStrategy
Public MustOverride Sub Play ()
End Class
AttackStrategy (ConcreteStrategy)
The code for the AttackStrategy
class is shown below. It is derived from TeamStrategy
.
Public Class AttackStrategy
Inherits TeamStrategy
Public Overrides Sub Play()
System.Console.WriteLine(" Playing in attacking mode")
End Sub
End Class
DefendStrategy (ConcreteStrategy)
The code for the DefendStrategy
class is shown below. It is derived from TeamStrategy
.
Public Class DefendStrategy
Inherits TeamStrategy
Public Overrides Sub Play()
System.Console.WriteLine(" Playing in defensive mode")
End Sub
End Class
Team (Context)
The code for the Team
class is shown below. A team can have only one strategy at a time according to our design.
Public Class Team
Private teamName As String
Private strategy As TeamStrategy
Public Sub SetStrategy(ByVal s As TeamStrategy)
strategy = s
End Sub
Public Sub PlayGame()
System.Console.WriteLine(teamName)
strategy.Play()
End Sub
Public Sub New(ByVal teamName As String)
Me.teamName = teamName
End Sub
End Class
Putting It All Together
This is the GameEngine
class to create teams, to set their strategies, and to make them play the game. The code is pretty simple, and commented heavily.
Public Class GameEngine
Public Shared Sub Main()
Dim attack As New AttackStrategy()
Dim defend As New DefendStrategy()
Dim france As New Team("France")
Dim italy As New Team("Italy")
System.Console.WriteLine("Setting the strategies..")
france.SetStrategy(attack)
italy.SetStrategy(defend)
france.PlayGame()
italy.PlayGame()
System.Console.WriteLine()
System.Console.WriteLine("Changing the strategies..")
france.SetStrategy(defend)
italy.SetStrategy(attack)
france.PlayGame()
italy.PlayGame()
System.Console.Read()
End Sub
End Class
Running the Project
Execute the project and you will get the following output:
Part IV
Applying the Decorator Pattern
In this section, we will see how to apply the Decorator pattern to solve our third design problem (just refer to the previous article if required). Our third design problem was related to assigning responsibilities (like Forward, Midfielder, etc.) to a player at runtime.
You can think about creating a player class, and then deriving sub classes like Forward, Midfielder, Defender, etc. But it is not the best solution, because as we discussed earlier, a player can be a forward at one time, and at some other time, the same player can be a mid-fielder. At least, it will be so in our soccer engine (any football experts around? ;)). So, these were our design problems:
Specific design problem: A player in a team should have additional responsibilities, like Forward, Defender etc., that can be assigned during runtime.
Problem generalized: We need to attach additional responsibilities (like Forward, Midfielder etc.) to the object (in this case, the Player
) dynamically, without using sub classing.
Understanding the Decorator Pattern
The Decorator pattern can be used to add responsibilities to objects dynamically. It also provides an excellent alternative to subclassing. The UML diagram for the Decorator pattern is shown below:
Fig. - Decorator Pattern
The participants of the pattern are detailed below:
Component
The Component
class indicates an abstract interface for components. Later, we attach additional responsibilities to these components.
ConcreteComponent
The ConcreteComponent
class is the concrete implementation of the Component
class. It actually defines an object to which additional responsibilities can be attached.
Decorator
The Decorator
class is derived from the Component
class. That means, it inherits all the interfaces (functions, properties, etc.) of the Component
. It also keeps a reference to an object which is inherited from the Component
class. Hence, one concrete decorator can keep references to other concrete decorators as well (because the Decorator
class is inherited from the Component
class).
ConcreteDecorator
This class is the actual place where we attach responsibilities to the component.
Adapting the Decorator Pattern
Now, it is time to adapt the Decorator pattern to solve our design problem related to the player.
Fig. - Solving Our Third Design Problem
You can see that we have two concrete components, GoalKeeper
and FieldPlayer
, inherited from the Player
class. We have three concrete decorators, Forward
, MidFielder
, and Defender
. For a team, we may need 11 field players and one goal keeper. Our design intend is, we need to assign responsibilities like Forward, Defender, etc., to the players during run time. We have only 11 field players - but it is possible that we can have 11 forwards and 11 midfielders at the same time, because a single player can be a forward and a midfielder at the same time. This will enable us to formulate good playing strategies - by assigning multiple roles to players, by swapping their roles, etc.
For example, you can ask a player to go forward and shoot a goal at some point of the match, by temporarily assigning him to a Forward
decorator.
To give additional responsibilities to a concrete component, first you create an object of the concrete component, and then you will assign it as the reference of a decorator. For example, you can create a field player and a mid fielder decorator, and then you can assign the field player to the mid fielder decorator to add the responsibility of the mid fielder to your player. Later, if you want, you can assign the same player to an object of a forward decorator. This is very well explained in the GameEngine module of the Decorator pattern sample code.
See the implementation below. It is heavily commented.
Decorator Pattern Implementation
Player (Component)
The implementation of the Player
class is shown below:
Public MustInherit Class Player
Private myName As String
Public Property Name() As String
Get
Return myName
End Get
Set(ByVal Value As String)
myName = Value
End Set
End Property
Public MustOverride Sub PassBall()
End Class
FieldPlayer (ConcreteComponent)
The implementation of the FieldPlayer
class is shown below:
Public Class FieldPlayer
Inherits Player
Public Overrides Sub PassBall ()
System.Console.WriteLine(_
" Fieldplayer ({0}) - passed the ball", _
MyBase.Name)
End Sub
Public Sub New(ByVal playerName As String)
MyBase.Name = playerName
End Sub
End Class
GoalKeeper (ConcreteComponent)
The implementation of the GoalKeeper
class is shown below:
Public Class GoalKeeper
Inherits Player
Public Overrides Sub PassBall ()
System.Console.WriteLine(" GoalKeeper " & _
"({0}) - passed the ball", MyBase.Name)
End Sub
Public Sub New(ByVal playerName As String)
MyBase.Name = playerName
End Sub
End Class
PlayerRole (Decorator)
The implementation of the PlayerRole
class is shown below:
Public Class PlayerRole
Inherits player
Protected player As player
Public Overrides Sub PassBall()
player.PassBall()
End Sub
Public Sub AssignPlayer(ByVal p As player)
player = p
End Sub
End Class
Forward (ConcreteDecorator)
The implementation of the Forward
class is shown below:
Public Class Forward
Inherits PlayerRole
Public Sub ShootGoal()
System.Console.WriteLine(" Forward ({0}) - " & _
"Shooted the ball to goalpost", _
MyBase.player.Name)
End Sub
End Class
MidFielder (ConcreteDecorator)
The implementation of the MidFielder
class is shown below:
Public Class MidFielder
Inherits PlayerRole
Public Sub Dribble()
System.Console.WriteLine(_
" Midfielder ({0}) - dribbled the ball", _
MyBase.player.Name)
End Sub
End Class
Defender (ConcreteDecorator)
The implementation of the Defender
class is shown below:
Public Class Defender
Inherits PlayerRole
Public Sub Defend()
System.Console.WriteLine(_
" Defender ({0}) - defended the ball", _
MyBase.player.Name)
End Sub
End Class
Putting It All Together
Public Class GameEngine
Public Shared Sub Main()
Dim owen As New FieldPlayer("Owen")
Dim beck As New FieldPlayer("Beckham")
Dim khan As New GoalKeeper("Khan")
System.Console.WriteLine()
System.Console.WriteLine(" > Warm up Session... ")
owen.PassBall()
beck.PassBall()
khan.PassBall()
System.Console.WriteLine()
System.Console.WriteLine(" > Match is starting.. ")
Dim forward1 As New Forward()
forward1.AssignPlayer(owen)
Dim midfielder1 As New MidFielder()
midfielder1.AssignPlayer(beck)
forward1.PassBall()
forward1.ShootGoal()
midfielder1.PassBall()
midfielder1.Dribble()
System.Console.WriteLine()
System.Console.WriteLine(" > OOps, Owen " _
"got injured. " & _
"Jerrard replaced Owen.. ")
Dim jerrard As New FieldPlayer("Jerrard")
forward1.AssignPlayer(jerrard)
forward1.ShootGoal()
Dim onemoreForward As New Forward()
onemoreForward.AssignPlayer(beck)
System.Console.WriteLine()
System.Console.WriteLine(" > Beckham has " & _
"multiple responsibilities.. ")
onemoreForward.ShootGoal()
midfielder1.Dribble()
System.Console.Read()
End Sub
End Class
Running the Project
After executing the project, you'll get the following output:
Conclusion
In this article, we discussed:
- Strategy pattern and its implementation
- Decorator pattern and its implementation
That is it for now. In fact, the overwhelming response from the CodeProject community to my first article inspired me to publish this one. Thank you everyone for your response and encouragement.
Appendix - Couple of interesting reads in my blog
History
- "One day you will also become part of the history. And others will remember only what you gave."
- Nov 09, 2005 - Prepared this article for publishing.