Tuesday, January 1, 2008

Strategy Design Pattern

Consider the following example – We need to develop the framework for a game design (Age of Empires is a good example) that requires simulation of individual characters. Let us start this design by considering a couple of character types – peasants and soldiers with the following characteristics for these characters
  • Both peasants and soldiers can eat and talk

  • Peasants can move slowly (well walk) while soldiers can move fast

  • Peasants cannot fight whereas soldiers can fight

A person well conversant with OOPS would start out by building this design based on a GameCharacter abstract class that will provide the default implementations for the common methods say eat and talk while leaving the implementation of methods such as move and fight to the sub classes.

GameCharacter Class Listing

Public MustInherit Class GameCharacter

  Public Sub Eat()

    Console.WriteLine("Character is eating")

  End Sub

 

  Public Sub Talk()

    Console.WriteLine("Character is talking")

  End Sub

 

  Public MustOverride Sub Move()

  Public MustOverride Sub Fight()

End Class



The Peasant class and the Soldier class can then inherit from the GameCharacter class providing concrete implementations of the Move and the Fight methods

Peasant Class Listing

Public Class Peasant

  Inherits GameCharacter

 

  Public Overrides Sub Fight()

    Console.WriteLine("Character cannot fight")

  End Sub

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is moving slowly")

  End Sub

End Class



Soldier Class Listing

Public Class Soldier

  Inherits GameCharacter

 

  Public Overrides Sub Fight()

    Console.WriteLine("Character is Fighting")

  End Sub

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is moving fast")

  End Sub

End Class



Let us now consider the introduction of a new character – the Cavalry man with the following characteristics
  • Cavalry man can fight

  • Cavalry man moves by riding a horse

Using our existing design, this is fairly easy to accomplish by creating another class – CavalryMan which implements the GameCharacter class and providing appropriate implementations for the Move and Fight methods. For sake of simplicity, I am ignoring the Horse at this point of time since it does not provide any independent behavior of its own.

CavalryMan Class Listing

Public Class CavalryMan

  Inherits GameCharacter

 

  Public Overrides Sub Fight()

    Console.WriteLine("Character is fighting")

  End Sub

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is riding a horse")

  End Sub

End Class



A quick view of the class diagram shows a fairly decent class hierarchy and the design seems to fit the requirements quite aptly.



If you review the code, you will find that both the Soldier and the CavalryMan provide the same implementation for the Fight method. Although, my example uses a single line of code to provide the appropriate implementation, real world classes will have complex logic that would be repeated in both these classes to provide this implementation. An alternative to this design might inspire a few programmers to suggest that we move the Fight method implementation to the GameCharacter abstract class and override this behavior in the Peasant sub-class. We can then consolidate the implementation at one location without need to rewrite the same implementation in the Soldier and CavalryMan sub-classes.

GameCharacter Class Listing

Public MustInherit Class GameCharacter

  Public Sub Eat()

    Console.WriteLine("Character is eating")

  End Sub

 

  Public Sub Talk()

    Console.WriteLine("Character is talking")

  End Sub

 

  Public Overridable Sub Fight()

    Console.WriteLine("Character is Fighting")

  End Sub

 

  Public MustOverride Sub Move()

End Class



Soldier Class Listing

Public Class Soldier

  Inherits GameCharacter

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is moving fast")

  End Sub

End Class



CavalryMan Class Listing

Public Class CavalryMan

  Inherits GameCharacter

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is riding a horse")

  End Sub

End Class



Clearly, the Peasant class requires no change and the original overriding of the Fight method provides its default behavior. The Class diagram is identical to our original class diagram barring the default method implementation of the Fight method and the missing implementations in the Soldier and the CavalryMan class.



Now, if we have to introduce another character – the Mason who exhibits the following characteristics
  • Mason cannot fight, a behavior that he shares with the peasant

  • Mason moves fast, a behavior that he shares with the soldier

Ah, we have a challenge at hand now. If we were to use our last idea, we could simply provide a default implementation of the Move method as well in the GameCharacter class, allowing the Mason and Soldier to move fast and therefore, requiring no special implementation of these methods in the Soldier and Mason classes. The Peasant and CavalryMan classes already provide their implementation of the Move method. The Fight method, would however pose a challenge of sorts. We would need to implement the behavior in the Mason class to override the default implementation in the GameCharacter class.

GameCharacter Class Listing

Public MustInherit Class GameCharacter

  Public Sub Eat()

    Console.WriteLine("Character is eating")

  End Sub

 

  Public Sub Talk()

    Console.WriteLine("Character is talking")

  End Sub

 

  Public Overridable Sub Fight()

    Console.WriteLine("Character is Fighting")

  End Sub

 

  Public Overridable Sub Move()

    Console.WriteLine("Character is moving fast")

  End Sub

End Class



Soldier Class Listing

Public Class Soldier

  Inherits GameCharacter

 

  'Wow no implementation,

  'does this mean the GameCharacter is a soldier by default. Hmm!

End Class



Mason Class Listing

Public Class Mason

  Inherits GameCharacter

 

  Public Overrides Sub Fight()

    Console.WriteLine("Character cannot fight")

  End Sub

End Class



Well, looks like we have a working solution to our requirement.



However, this design suffers from certain flaws that are readily visible. One of most obvious flaws is the GameCharacter representing the Soldier class by default. In a truly object oriented design, this could be read as GameCharacter and Soldier class being the same. Extending this statement to the implementing classes implies that
  • Peasant is a type of Soldier who walks slowly and does not fight

  • Mason is a type of Soldier who walks fast and does not fight

  • CavalryMan is a type of Soldier who rides instead of walking

Barring the last one pertaining to the CavalryMan, the other two definitions do not fit our logical definition of these characters.
The OOPS solution to this would be to increase the depth of inheritance by introducing an additional sub-class that can act as a super-type for characters that fight. The Soldier and CavalryMan could then extend from this FightingCharacter class while the Peasant and the Mason could extend from the GameCharacter Class.

GameCharacter Class Listing

Public MustInherit Class GameCharacter

  Public Sub Eat()

    Console.WriteLine("Character is eating")

  End Sub

 

  Public Sub Talk()

    Console.WriteLine("Character is talking")

  End Sub

 

  Public Overridable Sub Fight()

    Console.WriteLine("Character cannot fight")

  End Sub

 

  Public MustOverride Sub Move()

End Class



FightingCharacter Class Listing

Public MustInherit Class FightingCharacter

  Inherits GameCharacter

 

  Public Overrides Sub Fight()

    Console.WriteLine("Character is fighting")

  End Sub

End Class



Soldier Class Listing

Public Class Soldier

  Inherits FightingCharacter

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is moving fast")

  End Sub

End Class



CavalryMan Class Listing

Public Class CavalryMan

  Inherits FightingCharacter

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is riding a horse")

  End Sub

End Class



Peasant Class Listing

Public Class Peasant

  Inherits GameCharacter

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is moving slowly")

  End Sub

End Class



Mason Class Listing

Public Class Mason

  Inherits GameCharacter

 

  Public Overrides Sub Move()

    Console.WriteLine("Character is moving fast")

  End Sub

End Class



The default Fight implementation of the GameCharacter class would prevent the character from fighting and therefore, the Peasant and Mason class would not need to provide any implementation for the Fight method. The default Fight implementation of the FightingCharacter class would provide the necessary Fighting ability to the class overriding the implementation of the GameCharacter class. The Soldier and CavalryMan class would not be required to provide any implementation of the Fight method simply inheriting the implementation of the FightingCharacter.



However, we would still need to resolve the Move implementation where classes in different hierarchies – Soldier from the FightingCharacter->GameCharacter hierarchy and Mason from the GameCharacter hierarchy need to provide the same implementation.
This approach would therefore not work out for us. Even complicating the design by creating a FastMovingCharacter class which extends the Gaming character and provides a “is moving fast” implementation of the Move method, then extending the Mason class and trying to extend the Soldier from the same class is not possible. This is primarily because multiple inheritance is not available to us and even using a language permitting this would complicate the application since it won’t be clear about the exact nature of implementation.
This brings us to revealing the second flaw in the design. The use of overriding is a cautious exercise and overriding without understanding its impact might complicate the understanding of the classes and their exact implementation nature.
Well, so what is the best design for this little requirement? Please welcome the Strategy pattern. It helps address requirements of our characters – Peasant, Mason, Soldier and Cavalryman by translating their Move and Fight methods into strategies. Each Strategy is declared within an interface and an unlimited number of implementations are then possible for this strategy. In our example, we can declare …
  • A couple of strategies for Fighting – Is Fighting and Cannot Fight.

  • Three strategies for Moving – Moving Fast, Moving Slowly, Riding a horse

Each strategy interface provides the methods required for the strategy. Let us consider these interfaces - FightStrategy interface for providing the Fight functionality and the MoveStrategy interface for providing the Move functionality.

FightStrategy Interface Listing

Public Interface FightStrategy

  Sub Fight()

End Interface



MoveStrategy Interface Listing

Public Interface MoveStrategy

  Sub Move()

End Interface



We can then implement the actual Fight strategies using the RealFighter and the WimpyFella classes where each class implements the FightStrategy providing a unique implementation for the Fight method.

RealFighter Class Listing

Public Class RealFighter

  Implements FightStrategy

 

  Public Sub Fight() Implements FightStrategy.Fight

    Console.WriteLine("Character is fighting")

  End Sub

End Class



WimpyFella Class Listing

Public Class WimpyFella

  Implements FightStrategy

 

  Public Sub Fight() Implements FightStrategy.Fight

    Console.WriteLine("Character cannot fight")

  End Sub

End Class



In the same fashion, let us create the three Move strategies based on our requirement – FastMover, SlowMover and the RidingMover classes that implement the Move method of the MoveStrategy

FastMover Class Listing

Public Class FastMover

  Implements MoveStrategy

 

  Public Sub Move() Implements MoveStrategy.Move

    Console.WriteLine("Character is moving fast")

  End Sub

End Class



SlowMover Class Listing

Public Class SlowMover

  Implements MoveStrategy

 

  Public Sub Move() Implements MoveStrategy.Move

    Console.WriteLine("Character is moving slowly")

  End Sub

End Class



RidingMover Class Listing

Public Class RidingMover

  Implements MoveStrategy

 

  Public Sub Move() Implements MoveStrategy.Move

    Console.WriteLine("Character is riding a horse")

  End Sub

End Class



We can then declare instance variables in the original GameCharacter class to represent the Fight and Move strategies. I generally use the naming convention that prefixes class names with the lower case letter o to represent objects. I will declare two variables – oMoveStrategy and oFightStrategy in my GameCharacter class. The default implementation of the Eat and Talk method can remain unchanged while the Move and Fight methods will delegate the responsibility of using the appropriate strategy to the instance variables. Although, I have left the instance variables at a Protected access scope, you might decide to use a Private scope and restrict the access via get/set methods. For sake of simplicity, I have defined a constructor that requires the two strategies. This will prevent invocation of the object’s Fight and Move methods without setting the two strategies.

GameCharacter Class Listing

Public Class GameCharacter

  Protected oFightStrategy As FightStrategy

  Protected oMoveStrategy As MoveStrategy

 

  Public Sub New(ByVal oFightStrategy As FightStrategy, ByVal oMoveStrategy As MoveStrategy)

    With Me

      .oFightStrategy = oFightStrategy

      .oMoveStrategy = oMoveStrategy

    End With

  End Sub

 

  Public Sub Eat()

    Console.WriteLine("Character is eating")

  End Sub

 

  Public Sub Talk()

    Console.WriteLine("Character is talking")

  End Sub

 

  Public Sub Fight()

    oFightStrategy.Fight()

  End Sub

 

  Public Sub Move()

    oMoveStrategy.Move()

  End Sub

End Class



We can now redefine our characters to extend the Gamecharacter class and all we need to provide is the correct instantiation parameters for the instance of the child class.

Peasant Class Listing

Public Class Peasant

  Inherits GameCharacter

 

  Public Sub New()

    MyBase.New(New WimpyFella, New SlowMover)

  End Sub

End Class



Soldier Class Listing

Public Class Soldier

  Inherits GameCharacter

 

  Public Sub New()

    MyBase.New(New RealFighter, New FastMover)

  End Sub

End Class



CavalryMan Class Listing

Public Class CavalryMan

  Inherits GameCharacter

 

  Public Sub New()

    MyBase.New(New RealFighter, New RidingMover)

  End Sub

End Class



Mason Class Listing

Public Class Mason

  Inherits GameCharacter

 

  Public Sub New()

    MyBase.New(New WimpyFella, New FastMover)

  End Sub

End Class



Well, that’s all there is to it.



To demonstrate the functionality, I built a console application around these classes.

modMain Module Listing

Module modMain

  Sub Main()

    Dim oGC As GameCharacter

    Console.WriteLine("Peasant") : oGC = New Peasant : oGC.Fight() : oGC.Move()

    Console.WriteLine("Soldier") : oGC = New Soldier : oGC.Fight() : oGC.Move()

    Console.WriteLine("CavalryMan") : oGC = New CavalryMan : oGC.Fight() : oGC.Move()

    Console.WriteLine("Mason") : oGC = New Mason : oGC.Fight() : oGC.Move()

  End Sub

End Module



And the results of our sample test application

No comments: