Wednesday, January 2, 2008

Adapter Design Pattern

Let us extend our simulation game example and introduce some animals in addition to the Human characters. For example, Trained elephants or Elephants for short. These trained elephants share a common behavior with other animals, say, Sleep, Drink, Eat and Graze. These elephants, being trained for warfare can also Move and Fight similar to Human characters. For this example, I will consider the Soldier who implements the Eat, Talk, Fight and Move behavior from the GameCharacter interface.

GameCharacter Interface Listing

Public Interface GameCharacter

  Sub Eat()

  Sub Talk()

  Sub Fight()

  Sub Move()

End Interface



Soldier Class Listing

Public Class Soldier

  Implements GameCharacter

 

  Public Sub Eat() Implements GameCharacter.Eat

    Console.WriteLine("Soldier is eating")

  End Sub

 

  Public Sub Fight() Implements GameCharacter.Fight

    Console.WriteLine("Soldier is fighting")

  End Sub

 

  Public Sub Move() Implements GameCharacter.Move

    Console.WriteLine("Soldier is moving slowly")

  End Sub

 

  Public Sub Talk() Implements GameCharacter.Talk

    Console.WriteLine("Soldier is talking")

  End Sub

End Class



Let us assume that the Animal class and the inheriting sub-classes for specific animals were created by another developer on the team.

Animal Class Listing

Public MustInherit Class Animal

  Public Sub Sleep()

    Console.WriteLine("Animal is sleeping")

  End Sub

 

  Public Sub Drink()

    Console.WriteLine("Animal is drinking")

  End Sub

 

  Public Sub Eat()

    Console.WriteLine("Animal is eating")

  End Sub

 

  Public Sub Graze()

    Console.WriteLine("Animal is grazing")

  End Sub

 

  Public Sub MakeSound()

    Console.WriteLine("Animal is making some noise")

  End Sub

End Class



The TrainedElephant class extends the Animal class by adding the Move and Attack methods

TrainedElephant Class Listing

Public Class TrainedElephant

  Inherits Animal

 

  Public Sub Move()

    Console.WriteLine("TrainedElephant is moving")

  End Sub

 

  Public Sub Attack()

    Console.WriteLine("TrainedElephant is attack")

  End Sub

End Class



Let us have a quick look at our class and interface hierarchy



And finally, lets list our requirement. With an existing design that resembles this, we would like to maintain a list of all character instances in the game including humans as well as animals that can fight and move. A quick solution is to use a generic list that holds GameCharacter objects. Since Soldiers natively implement the GameCharacter interface, it is quite easy to add a Soldier to this list. However, there is no relation between the Animal class and the GameCharacter interface and neither does the TrainedElephant class implement the Animal interface. It, therefore makes it impossible to add the TrainedElephant as a GameCharacter to the list.



One solution is to modify the code for the TrainedElephant class so that it implements the GameCharacter Interface. Although this seems easy from the looks of it, this may not be practical in a real life application where this portion of the code is built by another developer or when the source code for this class is not available for modification. In such scenarios, we can employ the services of the Adapter pattern to adapt the functionality of the TrainedElephant class to map to the GameCharacter class.
The TrainedElephantAdapter class simply wraps around a TrainedElephant object while implementing the GameCharacter interface. Thus, we do not need to modify the source code for the TrainedElephant or the Animal class and can use this TrainedElephantAdapter object interchangeably as a GameCharacter.

TrainedElephantAdapter Class Listing

Public Class TrainedElephantAdapter

  Implements GameCharacter

 

  Private _oElephant As TrainedElephant

 

  Public Sub New(ByVal oElephant As TrainedElephant)

    _oElephant = oElephant

  End Sub

 

  Public Sub Eat() Implements GameCharacter.Eat

    _oElephant.Eat()

  End Sub

 

  Public Sub Fight() Implements GameCharacter.Fight

    _oElephant.Attack()

  End Sub

 

  Public Sub Move() Implements GameCharacter.Move

    _oElephant.Move()

  End Sub

 

  Public Sub Talk() Implements GameCharacter.Talk

    _oElephant.MakeSound()

  End Sub

End Class




Our class diagram now contains our new TrainedElephantAdapter which encapsulates the TrainedElephant object while implementing the GameCharacter interface



Finally, a little test harness using the TrainedElephantAdapter to do the magic for us

modMain Module Listing

Module modMain

  Sub Main()

    Dim GameCharacters As List(Of GameCharacter) = New List(Of GameCharacter)

    GameCharacters.Add(New Soldier)

    'And here is the Wrapped up TrainedElephant

    GameCharacters.Add(New TrainedElephantAdapter(New TrainedElephant))

 

    For Each oGC As GameCharacter In GameCharacters

      oGC.Eat() : oGC.Fight() : oGC.Move() : oGC.Talk()

    Next

    Console.ReadLine()

  End Sub

End Module



And the results of using the Adapter pattern

No comments: