Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Design Your Soccer Engine, and Learn How To Apply Design Patterns (Observer, Decorator, Strategy, and Builder Patterns) - Part III and IV

4.87/5 (70 votes)
21 Oct 2009CPOL9 min read 1   607  
This article is a continuation of the previous article, and in this article, we will discuss (1) Applying the Strategy pattern to solve design problems related with 'Team' and 'TeamStrategy', and (2) Applying the Decorator pattern to solve design problems related with the 'Player'.

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.

Image 1

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:

Image 2

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.

Image 3

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:

VB
'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 the AttackStrategy class is shown below. It is derived from TeamStrategy.

VB
'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 the DefendStrategy class is shown below. It is derived from TeamStrategy.

VB
'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 the Team class is shown below. A team can have only one strategy at a time according to our design.

VB
'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.

VB
'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 will get the following output:

Image 4


Image 5

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:

Image 6

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.

Image 7

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:

VB
' 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 the FieldPlayer class is shown below:

VB
' 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 the GoalKeeper class is shown below:

VB
' 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 the PlayerRole class is shown below:

VB
'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 the Forward class is shown below:

VB
'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 the MidFielder class is shown below:

VB
'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 the Defender class is shown below:

VB
'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

VB
'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:

Image 8

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)