Wednesday, January 2, 2008

Facade Design Pattern

The Facade pattern is probably one of the most frequently used design patterns. When a class requires the functionality derived from different classes, it is often useful to use a façade class that encapsulates the calls to the various classes. This class can then be effectively used instead of making calls to the individual classes and improves code reuse. Additionally, if we decide to make changes to the encapsulated classes, all we need to alter are the method invocations in the façade class.
Consider a business application for a mortgage company. The Mortgage Company uses multiple services that are both internal to the company as well as require interaction with other parties to determine if a loan can be sanctioned. Using the Facade pattern here is easy, create a class say LoanHelper. We can then create shared method unless you prefer to create an instance of the LoanHelper. Add the method calls to the different internal and external services. This LoanHelper method can then be used anywhere within your code to validate a prospect before allotting a loan and determining the rate of interest.

LoanHelper Class Listing

Public Class LoanHelper

 

  Public Const DEFAULT_LOAN_PERCENT As Double = 10.0

 

  Public Shared Function GetEligbleLoanCriteria(ByVal SSN As String, ByVal RequiredLoanAmount As Double, _

  ByRef EligibleLoanAmount As Double, ByRef EligibleLoanPercent As Double) As Boolean

    'Add the method calls to various systems - internal and external to determine

    'the values of EligibleLoanAmount and the EligibleLoanAmount

    EligibleLoanAmount = 100000

    EligibleLoanPercent = 8.25

    Console.WriteLine("Façade class LoanHelper was invoked to determine Eligible Loan Criteria")

    Return True  'Return false if SSN failed to match

  End Function

End Class



modMain Module Listing

Module modMain

  Sub Main()

    Dim SSN As String = "SampleSSN"

    Dim RequiredLoanAmount As Double = 100000

    Dim EligibleLoanAmount As Double = 0

    Dim EligibleLoanPercent As Double = LoanHelper.DEFAULT_LOAN_PERCENT

    Dim IsSSNMatch As Boolean = LoanHelper.GetEligbleLoanCriteria(SSN, RequiredLoanAmount, _

    EligibleLoanAmount, EligibleLoanPercent)

    Console.ReadLine()

  End Sub

End Module



A look at the classes


And the results

State Design Pattern

For this example, let us assume that we would like to design an application that simulates the interaction in a single character game. The character, in this game, can be moving on the ground, swimming under water, flying an airplane or driving a car. Each gaming episode may involve the character in any one of these conditions. The character is expected to navigate in these different environments using the same navigation keys on the keyboard. Additionally, the character may use the Action Key (say Control key on the keyboard) to perform actions that are relevant to each environment. While on ground, the Action Key is used to talk; while in water, it is used to float up for a breath of air, while in an airplane, to get directions and while in the car, to use the GPS.
A map of the keys versus environments is listed here

Key Ground Underwater Airplane Car 
Front Direction Move Forward Swim Forward Increase Throttle Accelerate 
Back Direction Move Backward Swim Backward Decrease Throttle Decelerate 
Right Direction Move Angular Right Swim Angular Right Change Direction to the Right Turn Car to the Right 
Left Direction Move Angular Left Swim Angular Left Change Direction to the Left Turn Car to the Left 
Action Talk Surface for Air Get directions Use GPS 

A first take at this design would be by starting off creating a GameCharacter class. The interactions that are possible would be based on the key strokes that the GameCharacter supports. Let me map them to the normal gaming keys on the keyboard – UpArrowPressed, DownArrowPressed, RightPressed and LeftPressed to cover the navigation areas and the ActionPressed method for supporting the character’s action feature. The State of the environment can be stored in an instance value that holds an enumeration.

GameCharacter Class Listing

Public Class GameCharacter

  Private Enum EnvironmentStates

    Land

    Underwater

    Airplane

    Car

  End Enum

 

  Private _EnvironmentState As EnvironmentStates

 

  Public Sub New()

    'All episodes start with the character on Land

    _EnvironmentState = EnvironmentStates.Land

  End Sub

 

  Public Sub UpArrowPressed()

    Select Case _EnvironmentState

      Case EnvironmentStates.Land

        Console.WriteLine("Character is moving forward")

      Case EnvironmentStates.Underwater

        Console.WriteLine("Character is swimming forward")

      Case EnvironmentStates.Airplane

        Console.WriteLine("Character is increasing throttle")

      Case EnvironmentStates.Car

        Console.WriteLine("Character is accelerating")

    End Select

  End Sub

 

  Public Sub DownArrowPressed()

    Select Case _EnvironmentState

      Case EnvironmentStates.Land

        Console.WriteLine("Character is moving backwards")

      Case EnvironmentStates.Underwater

        Console.WriteLine("Character is swimming backwards")

      Case EnvironmentStates.Airplane

        Console.WriteLine("Character is decreasing throttle")

      Case EnvironmentStates.Car

        Console.WriteLine("Character is decelerating")

    End Select

  End Sub

 

  Public Sub RightArrowPressed()

    Select Case _EnvironmentState

      Case EnvironmentStates.Land

        Console.WriteLine("Character is moving at an angular right")

      Case EnvironmentStates.Underwater

        Console.WriteLine("Character is swimming at an angular right")

      Case EnvironmentStates.Airplane

        Console.WriteLine("Character is changing directionto the right")

      Case EnvironmentStates.Car

        Console.WriteLine("Character is turning car to the right")

    End Select

  End Sub

 

  Public Sub LeftArrowPressed()

    Select Case _EnvironmentState

      Case EnvironmentStates.Land

        Console.WriteLine("Character is moving at an angular left")

      Case EnvironmentStates.Underwater

        Console.WriteLine("Character is swimming at an angular left")

      Case EnvironmentStates.Airplane

        Console.WriteLine("Character is changing directionto the left")

      Case EnvironmentStates.Car

        Console.WriteLine("Character is turning car to the left")

    End Select

  End Sub

 

  Public Sub ActionPressed()

    Select Case _EnvironmentState

      Case EnvironmentStates.Land

        Console.WriteLine("Character is talking")

      Case EnvironmentStates.Underwater

        Console.WriteLine("Character is surfacing for air")

      Case EnvironmentStates.Airplane

        Console.WriteLine("Character is getting directions")

      Case EnvironmentStates.Car

        Console.WriteLine("Character is using the GPS")

    End Select

  End Sub

End Class



Additionally, while in water, if the character reaches the edge of the water body, user will jump out of water and be on solid ground. Similarly, reaching the landing area, whilst in an airplane, will move the character on the ground. In a similar fashion, the character will change environments when he reaches the periphery of that environment. Ground is compatible with all the three environments and changes are triggered in both directions on reaching appropriate boundaries. Environment changes between other boundaries are not possible.

Environment Water boundary Airport boundary Parking Lot boundary
Land Jump underwater Jump inside Airplane Jump inside Car
Underwater Jump on Land* Not Applicable Not Applicable
Airplane Not Applicable Jump on Land* Not Applicable
Car Not Applicable Not Applicable Jump on Land*

* Next corresponding boundary

We can extend our design to add instance variables that can help define the boundary parameters. Note that while on land – a two dimensional co-ordinate system can be used to map the boundary, however while in water or in air, we need a three dimensional co-ordinate system to store the depth and height respectively in addition to the x and y co-ordinates.

Additional Instance variables

  Private _XCoord As Integer

  Private _YCoord As Integer

  Private _DepthCoord As Integer

  Private _HeightCoord As Integer

  Private _OpenLandXDimension As Integer

  Private _OpenLandYDimension As Integer

  Private _WaterXDimension As Integer

  Private _WaterYDimension As Integer

  Private _WaterDepthDimension As Integer

  Private _AirplaneXDimension As Integer

  Private _AirplaneYDimension As Integer

  Private _AirplaneHeightDimension As Integer

  Private _RoadXDimension As Integer

  Private _RoadYDimension As Integer



Modified constructor

  Public Sub New()

    'All episodes start with the character on Land

    _EnvironmentState = EnvironmentStates.Land

    ' ... at location 0,0; isn't that nice.

    _XCoord = 0 : _YCoord = 0 : _DepthCoord = 0 : _HeightCoord = 0

 

    'Define the dimensions of each environment

    _OpenLandXDimension = 100 : _OpenLandYDimension = 100

    _WaterXDimension = 100 : _WaterYDimension = 100 : _WaterDepthDimension = 50

    _AirplaneXDimension = 100 : _AirplaneYDimension = 100 : _AirplaneHeightDimension = 200

    _RoadXDimension = 50 : _RoadYDimension = 200

  End Sub



Boundary determining functions

  Private Function _IsWaterBoundaryEncountered() As Boolean

    'Use the layout map and the current X and Y coordinates and the Depth coordinate to determine if the water boundary is encountered

    'the depth coordinate is useful when the current state is in water

  End Function

 

  Private Function _IsAirportBoundaryEncountered() As Boolean

    'Use the layout map and the current X and Y coordinates as well as the Height coordinate to determine if the airport boundary is encountered

    'the height coordindate is useful when the current state is in the airplane

  End Function

 

  Private Function _IsParkingLotBoundaryEncountered() As Boolean

    'Use the layout map and the current X and Y coordinates as well as the coordinate to determine if the parking lot boundary is encountered

  End Function



Partially modified listing of the UpArrowPressed method

  Public Sub UpArrowPressed()

    Select Case _EnvironmentState

      Case EnvironmentStates.Land

        Console.WriteLine("Character is moving forward")

        _XCoord += 1

 

        Select Case True

          Case _IsWaterBoundaryEncountered()

            _EnvironmentState = EnvironmentStates.Underwater

            _XCoord = 0 : _YCoord = 0 : _DepthCoord = 1

 

          Case _IsAirportBoundaryEncountered()

            _EnvironmentState = EnvironmentStates.Airplane

            _XCoord = 0 : _YCoord = 0 : _HeightCoord = 1

 

          Case _IsParkingLotBoundaryEncountered()

            _EnvironmentState = EnvironmentStates.Car

            _XCoord = 0 : _YCoord = 0

        End Select

 

      Case EnvironmentStates.Underwater

        Console.WriteLine("Character is swimming forward")

        'Repeat similar code here

 

      Case EnvironmentStates.Airplane

        Console.WriteLine("Character is increasing throttle")

        'Repeat similar code here

 

      Case EnvironmentStates.Car

        Console.WriteLine("Character is accelerating")

        'Repeat similar code here

    End Select

  End Sub



Of course, besides navigating in these environments, the character performs a number of actions that are not relevant in the context of this example and I have deliberately avoided them (at least for now)
Well, we have a working design and things look good. A closer look at the code brings out the following issues –
  • The instance variable definition shows that a lot of variables only have contextual meaning based on the state of the character. For example, the _DepthCoord and the _HeightCoord have no meaning when the GameCharacter is on ground or while he is driving a car.

  • Each supported keystroke handler has the same “Select Case” code blocks to determine their action based on the state of the GameCharacter.

  • State transitions are interspersed in code making it almost impossible to understand when a transition takes place.

  • Adding a new state – say Sailing in a boat will require rework of each keystroke handler and additional rework on maintaining the correct state when the boundary is encountered. This implies that the entire GameCharacter class will need to be checked for changes and at least five methods will need to be altered in addition to adding instance variables to represent state specific behavior.

So how do we fix this? Quite easily, as a matter of fact, all we need to do is build a state interface that provides methods to address the keystrokes and implement each required state to handle the keystrokes as it sees fit in its environment. The GameCharacter simply holds the instance of the current state and delegates all keystrokes to the current state instance.

IEnvironmentState Interface Listing

Public Interface IEnvironmentState

  Sub UpArrowPressed()

  Sub DownArrowPressed()

  Sub RightArrowPressed()

  Sub LeftArrowPressed()

  Sub ActionPressed()

End Interface



Move the responsibility of holding and determining the environment boundaries to the GameEnvironmentManager class

GameEnvironmentManager Class Listing

Public Class GameEnvironmentManager

 

  Private _OpenLandXDimension As Integer

  Private _OpenLandYDimension As Integer

  Private _WaterXDimension As Integer

  Private _WaterYDimension As Integer

  Private _WaterDepthDimension As Integer

  Private _AirplaneXDimension As Integer

  Private _AirplaneYDimension As Integer

  Private _AirplaneHeightDimension As Integer

  Private _RoadXDimension As Integer

  Private _RoadYDimension As Integer

 

  Public Sub New()

    'Define the dimensions of each environment

    _OpenLandXDimension = 100 : _OpenLandYDimension = 100

    _WaterXDimension = 100 : _WaterYDimension = 100 : _WaterDepthDimension = 50

    _AirplaneXDimension = 100 : _AirplaneYDimension = 100 : _AirplaneHeightDimension = 200

    _RoadXDimension = 50 : _RoadYDimension = 200

  End Sub

 

  Public Function IsWaterBoundaryEncountered(ByVal XCoord As Integer, ByVal YCoord As Integer) As Boolean

    'Use the X and Y coord to determine if the Water boundary is encountered

  End Function

 

  Public Function IsWaterBoundaryEncountered(ByVal XCoord As Integer, ByVal YCoord As Integer, ByVal DepthCoord As Integer) As Boolean

    'Use the X and Y coord alongwith the depth to determine if the Water boundary is encountered

  End Function

 

  'Other functions to determine the other boundaries

End Class



Implement each state and control the state of the GameCharacter appropriately by creating classes for WalkingLand, SwimmingUnderWater, FlyingAirplane and DrivingCar

WalkingLand Class Listing

Public Class WalkingLand

  Implements IEnvironmentState

 

  Private _oGameCharacter As GameCharacter

  Private _oGEM As GameEnvironmentManager

  Private _XCoord As Integer

  Private _YCoord As Integer

 

  Public Sub New(ByVal oGameCharacter As GameCharacter, ByVal oGEM As GameEnvironmentManager)

    _oGameCharacter = oGameCharacter

    _oGEM = oGEM

    _XCoord = 0

    _YCoord = 0

  End Sub

 

  Public Sub UpArrowPressed() Implements IEnvironmentState.UpArrowPressed

    Console.WriteLine("Character is moving forward")

    _XCoord += 1

    _HandleIfBoundaryEncountered()

  End Sub

 

  Public Sub DownArrowPressed() Implements IEnvironmentState.DownArrowPressed

    Console.WriteLine("Character is moving backwards")

    _XCoord -= 1

    _HandleIfBoundaryEncountered()

  End Sub

 

  Public Sub RightArrowPressed() Implements IEnvironmentState.RightArrowPressed

    Console.WriteLine("Character is moving at an angular right")

    _XCoord += 1 : _YCoord -= 1

    _HandleIfBoundaryEncountered()

  End Sub

 

  Public Sub LeftArrowPressed() Implements IEnvironmentState.LeftArrowPressed

    Console.WriteLine("Character is moving at an angular left")

    _XCoord += 1 : _YCoord += 1

    _HandleIfBoundaryEncountered()

  End Sub

 

  Public Sub ActionPressed() Implements IEnvironmentState.ActionPressed

    Console.WriteLine("Character is talking")

  End Sub

 

  Private Sub _HandleIfBoundaryEncountered()

    Select Case True

      Case _oGEM.IsWaterBoundaryEncountered(_XCoord, _YCoord)

        _oGameCharacter.EnvironmentState = New SwimmingUnderwater(_oGameCharacter, _oGEM)

 

        'Other boundary encountered statements go here

    End Select

  End Sub

End Class



GameCharacter Class Listing

Public Class GameCharacter

  Private _oEnvironmentState As IEnvironmentState

  Private _oGEM As GameEnvironmentManager

 

  Public Sub New()

    _oGEM = New GameEnvironmentManager

    'All episodes start with the character on Land

    _oEnvironmentState = New WalkingLand(Me, _oGEM)

  End Sub

 

  Public Sub UpArrowPressed()

    _oEnvironmentState.UpArrowPressed()

  End Sub

 

  Public Sub DownArrowPressed()

    _oEnvironmentState.DownArrowPressed()

  End Sub

 

  Public Sub RightArrowPressed()

    _oEnvironmentState.RightArrowPressed()

  End Sub

 

  Public Sub LeftArrowPressed()

    _oEnvironmentState.DownArrowPressed()

  End Sub

 

  Public Sub ActionPressed()

    _oEnvironmentState.ActionPressed()

  End Sub

 

  Public WriteOnly Property EnvironmentState() As IEnvironmentState

    Set(ByVal value As IEnvironmentState)

      _oEnvironmentState = value

    End Set

  End Property

End Class



I have ignored the details of the other classes to reduce the code bulk in this example. This design completely separates the GameCharacter from its constituent states and the behavior of the GameCharacter is delegated to the individual states. Interestingly, each state may need to be aware of other related transitioning states in the system so that a state transition can be effectively addressed. An alternative to our design is to have these states predefined in the GameCharacter class and simply trigger the GameCharacter class via public methods to set its next state appropriately. This alternate design will eliminate any need for the implementing EnvironmentStates to know about each other and the entire controlling mechanism is built in the GameCharacter class.
Selecting an appropriate design choice depends on how best you feel about handling additional states or packaging components.



I have built a test harness to test our GameCharacter and its reaction based on Keystrokes. For the sake of keeping the example short, I have forced a return value of true for the boundary condition tests when the GameCharacter moves forward and when it surfaces for air after swimming backward.

modMain Module Listing

Module modMain

  Sub Main()

    Dim oGameCharacter As GameCharacter = New GameCharacter

    oGameCharacter.UpArrowPressed()  'This will trigger Swimming in water

    oGameCharacter.DownArrowPressed()

    oGameCharacter.ActionPressed() 'This will trigger Jumping on Land

    oGameCharacter.UpArrowPressed()  'This will trigger Swimming in water again, but we wont test it

    Console.ReadLine()

  End Sub

End Module



And the results

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