Applying Design Patterns - Part III and IV
Contents
- Introduction
- Part III
- Part IV
- Conclusion
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) - Part I and II
In my first article (which constitutes Part 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 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 the previous article at this point - just to remind yourself regarding 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 strategy pattern to solve the above design problem, because it lets the algorithm (i.e, the Team's strategy) vary independently from clients (i.e, the Team) that use it. Let us see how we can apply Strategy pattern to solve this design problem.
Understanding the Strategy Pattern
Strategy pattern is pretty simple. The UML diagram of 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 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
This Context can be configured with one or more concrete strategy. 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. The 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 lead by a number of goals - huh, well, I'm not a good football coach anyway). When we call PlayGame function in the Team, it calls the Play function of the current strategy. Kindly have a look at the code. It is straight forward, and everything is commented neatly.
By using 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 TeamStrategy class is shown below.
'Strategy: The TeamStrategy class
'This class provides an abstract interface
'to implement concrete strategy algorithms
Public MustInherit Class TeamStrategy
'AlgorithmInterface : This is the interface provided
Public MustOverride Sub Play ()
End Class ' END CLASS DEFINITION TeamStrategy
AttackStrategy (ConcreteStrategy)
The code for AttackStrategy class is shown below. It is derived from TeamStrategy
'ConcreteStrategy: The AttackStrategy class
'This class is a concrete implementation of the
'strategy class.
Public Class AttackStrategy
Inherits TeamStrategy
'Overrides the Play function.
'Let us play some attacking game
Public Overrides Sub Play()
'Algorithm to attack
System.Console.WriteLine(" Playing in attacking mode")
End Sub
End Class ' END CLASS DEFINITION AttackStrategy
DefendStrategy (ConcreteStrategy)
The code for DefendStrategy class is shown below. It is derived from TeamStrategy
'ConcreteStrategy: The DefendStrategy class
'This class is a concrete implementation of the
'strategy class.
Public Class DefendStrategy
Inherits TeamStrategy
'Overrides the Play function.
'Let us go defensive
Public Overrides Sub Play()
'Algorithm to defend
System.Console.WriteLine(" Playing in defensive mode")
End Sub
End Class ' END CLASS DEFINITION DefendStrategy
Team (Context)
The code for Team class is shown below. A team can have one strategy at a time, according to our design.
'Context: The Team class
'This class encapsulates the algorithm
Public Class Team
'Just a variable to keep the name of team
Private teamName As String
'A reference to the strategy algorithm to use
Private strategy As TeamStrategy
'ContextInterface to set the strategy
Public Sub SetStrategy(ByVal s As TeamStrategy)
'Set the strategy
strategy = s
End Sub
'Function to play
Public Sub PlayGame()
'Print the team's name
System.Console.WriteLine(teamName)
'Play according to the strategy
strategy.Play()
End Sub
'Constructor to create this class, by passing the team's
'name
Public Sub New(ByVal teamName As String)
'Set the team name to use later
Me.teamName = teamName
End Sub
End Class ' END CLASS DEFINITION Team
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.
'GameEngine class for demonstration
Public Class GameEngine
Public Shared Sub Main()
'Let us create a team and set its strategy,
'and make the teams play the game
'Create few strategies
Dim attack As New AttackStrategy()
Dim defend As New DefendStrategy()
'Create our teams
Dim france As New Team("France")
Dim italy As New Team("Italy")
System.Console.WriteLine("Setting the strategies..")
'Now let us set the strategies
france.SetStrategy(attack)
italy.SetStrategy(defend)
'Make the teams start the play
france.PlayGame()
italy.PlayGame()
System.Console.WriteLine()
System.Console.WriteLine("Changing the strategies..")
'Let us change the strategies
france.SetStrategy(defend)
italy.SetStrategy(attack)
'Make them play again
france.PlayGame()
italy.PlayGame()
'Wait for a key press
System.Console.Read()
End Sub
End Class
Running The Project
Execute the project and you'll get the following output.
Part IV
Applying Decorator Pattern
In this section, we will see how to apply the Decorator pattern to solve our third design problem (Just refer 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 the runtime."
Problem Generalized: "We need to attach additional responsibilities (like Forward, Midfielder etc) to the object (In this case, the Player) dynamically, with out using sub classing"
Understanding Decorator Pattern
Decorator pattern can be used to add responsibilities to objects dynamically. They also provide an excellent alternative to sub classing. The UML diagram of 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
Decorator class is derived from 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 Decorator class is inherited from the Component class).
- Concrete Decorator
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 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 Player class is shown below
' Component: The Player class
Public MustInherit Class Player
'Just give a name for this player
Private myName As String
'The property to get/set the name
Public Property Name() As String
Get
Return myName
End Get
Set(ByVal Value As String)
myName = Value
End Set
End Property
'This is the Operation in the component
'and this will be overrided by concrete components
Public MustOverride Sub PassBall()
End Class ' END CLASS DEFINITION Player
FieldPlayer (ConcreteComponent)
The implementation of FieldPlayer class is shown below
' ConcreteComponent : Field Player class
'This is a concrete component. Later, we will add additional responsibilities
'like Forward, Defender etc to a field player.
Public Class FieldPlayer
Inherits Player
'Operation: Overrides PassBall operation
Public Overrides Sub PassBall ()
System.Console.WriteLine(" Fieldplayer ({0}) - passed the ball", _
MyBase.Name)
End Sub
'A constructor to accept the name of the player
Public Sub New(ByVal playerName As String)
MyBase.Name = playerName
End Sub
End Class ' END CLASS DEFINITION FieldPlayer
GoalKeeper (ConcreteComponent)
The implementation of GoalKeeper class is shown below
' ConcreteComponent : GaolKeeper class
'This is a concrete component. Later, we can add additional responsibilities
'to this class if required.
Public Class GoalKeeper
Inherits Player
'Operation: Overriding the base class operation
Public Overrides Sub PassBall ()
System.Console.WriteLine(" GoalKeeper ({0}) - passed the ball", MyBase.Name)
End Sub
'A constructor to accept the name of the player
Public Sub New(ByVal playerName As String)
MyBase.Name = playerName
End Sub
End Class ' END CLASS DEFINITION GoalKeeper
PlayerRole (Decorator)
The implementation of PlayerRole class is shown below
'Decorator: PlayerRole is the decorator
Public Class PlayerRole
Inherits player
'The reference to the player
Protected player As player
'Call the base component's function
Public Overrides Sub PassBall()
player.PassBall()
End Sub
'This function is used to assign a player to this role
Public Sub AssignPlayer(ByVal p As player)
'Keep a reference to the player, to whom this
'role is given
player = p
End Sub
End Class ' END CLASS DEFINITION PlayerRole
Forward (ConcreteDecorator)
The implementation of Forward class is shown below
'ConcreteDecorator: Forward class is a Concrete implementation
'of the PlayerRole (Decorator) class
Public Class Forward
Inherits PlayerRole
'Added Behavior: This is a responsibility exclusively for the Forward
Public Sub ShootGoal()
System.Console.WriteLine(" Forward ({0}) - Shooted the ball to goalpost", _
MyBase.player.Name)
End Sub
End Class ' END CLASS DEFINITION Forward
MidFielder (ConcreteDecorator)
The implementation of MidFielder class is shown below
'ConcreteDecorator: MidFielder class is a Concrete implementation
'of the PlayerRole (Decorator) class
Public Class MidFielder
Inherits PlayerRole
'AddedBehavior: This is a responsibility exclusively for the Midfielder
'(Don't ask me whether only mid filders can dribble the ball - atleast
'it is so in our engine)
Public Sub Dribble()
System.Console.WriteLine(" Midfielder ({0}) - dribbled the ball", _
MyBase.player.Name)
End Sub
End Class ' END CLASS DEFINITION Midfielder
Defender (ConcreteDecorator)
The implementation of Defender class is shown below
'ConcreteDecorator: Defender class is a Concrete implementation
'of the PlayerRole (Decorator) class
Public Class Defender
Inherits PlayerRole
'Added Behavior: This is a responsibility exclusively for the Defender
Public Sub Defend()
System.Console.WriteLine(" Defender ({0}) - defended the ball", _
MyBase.player.Name)
End Sub
End Class ' END CLASS DEFINITION Defender
Putting It All Together
'Let us put it together
Public Class GameEngine
Public Shared Sub Main()
'-- Step 1:
'Create few players (concrete components)
'Create few field Players
Dim owen As New FieldPlayer("Owen")
Dim beck As New FieldPlayer("Beckham")
'Create a goal keeper
Dim khan As New GoalKeeper("Khan")
'-- Step 2:
'Just make them pass the ball
'(during a warm up session ;))
System.Console.WriteLine()
System.Console.WriteLine(" > Warm up Session... ")
owen.PassBall()
beck.PassBall()
khan.PassBall()
'-- Step 3: Create and assign the responsibilities
'(when the match starts)
System.Console.WriteLine()
System.Console.WriteLine(" > Match is starting.. ")
'Set owen as our first forward
Dim forward1 As New Forward()
forward1.AssignPlayer(owen)
'Set Beckham as our midfielder
Dim midfielder1 As New MidFielder()
midfielder1.AssignPlayer(beck)
'Now, use these players to do actions
'specific to their roles
'Owen can pass the ball
forward1.PassBall()
'And owen can shoot as well
forward1.ShootGoal()
'Beckham can pass ball
midfielder1.PassBall()
'Beckham can dribble too
midfielder1.Dribble()
' [ Arrange the above operations to some meaningfull sequence, like
' "Beckham dribbled and passed the ball to owen and owen shooted the
' goal ;) - just for some fun ]"
'-- Step 4: Now, changing responsibilities
'(during a substitution)
'Assume that owen got injured, and we need a new player
'to play as our forward1
System.Console.WriteLine()
System.Console.WriteLine(" > OOps, Owen got injured. " & _
"Jerrard replaced Owen.. ")
'Create a new player
Dim jerrard As New FieldPlayer("Jerrard")
'Ask Jerrard to play in position of owen
forward1.AssignPlayer(jerrard)
forward1.ShootGoal()
'-- Step 5: Adding multiple responsibilities
'(When a player need to handle multiple roles)
'We already have Beckham as our midfielder.
'Let us ask him to play as an additional forward
Dim onemoreForward As New Forward()
onemoreForward.AssignPlayer(beck)
System.Console.WriteLine()
System.Console.WriteLine(" > Beckham has multiple responsibilities.. ")
'Now Beckham can shoot
onemoreForward.ShootGoal()
'And use his earlier responsibility to dribble too
midfielder1.Dribble()
'According to our design, you can attach the responsibility of
'a forward to a goal keeper too, but when you actually
'play football, remember that it is dangerous ;)
'Wait for key press
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 over whelming response from the code project community to my first article inspired me to publish this one. Thank you for everyone for your response and encouragement.
4 comments:
Hey Anoop please do it for builder pattern too
Yes, very nice set of articles. I have one question though. I think the decorator pattern allow you only to add new behaviours/responsibilities/abilities whatever. How the heck do you remove a concrete decorator from the object? This is bugging me.
Hi,
I've read parts 1 and 2 - very interesting and usefull. Unfortunately pictures in parts 3 and 4 are missing - can someone fix it ?
This is nice article with nice example please also complete it for Builder pattern and also send links for other GoF pattern with such a nice examples.
Post a Comment