AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=993&pId=-1
Understanding Adapter Pattern Using .NET
page
by David Simmonds
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 54261/ 137

Introduction

This article examines the usage of Adapter pattern using Visual Basic .NET 2003 with the help of a sample application.

Intent according to the GoF

"Convert the interface of a class into another interface clients expect.  Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces"

Intent seen through Real Life

Makeup is the classic adapter; it converts the interface of the actor (facial-makeup for actors and body-airbrushing for models) into the interface expected (perfect pretty people) by their client (viewers/audience).

Behind the scenes, a car’s odometer converts the number of turns of the wheel into a representation of the number of miles the driver has traveled.  How does it do this?  A cable runs from the gearbox up to the dash-board of the car and turns a series of minute gears called cog-wheels.  Naturally, since the engine is turning the gears in the gearbox, it also turns the speedo-cable and for every couple of hundred turns of the wheel, the mile-digit of the odometer kicks over and shows a mile.  The speedometer is a bit trickier. Based on Faraday’s principle, electromagnets within the speedometer (again turned by the speedo-cable) turn past a stationary magnet.  The faster the cable spins, the faster the magnets crash through the stationary magnetic field and the greater the voltage induced in the electromagnet.  Your speedometer cable is in effect a crude electricity generator/voltmeter since the speedo-needle simply measures the voltage induced in the magnet.

By now you are wondering whether this tutorial has gone 50 mph down the wrong road.  Not quite, you see another adapter that many folks will be familiar with is the speedometer and odometer. These devices do several adaptations behind the scenes in a manner which makes for seamless interpretation of speed and distance traveled respectively.  But in reality, they are not measuring speed, but the number of turns of the wheel.  This is why if you jack up the car and turn the wheel, your “speed” seems to go from 0 to 10mph or so.

This illustrates the workings of the adapter pattern quite nicely.  Supposing I said my 14 year old carbureted car (properly tuned) gives me 31,000 wheel-turns per gallon in the city/43000 WTPG on the highway.  Admit it, you would be quite perplexed.  However, if I told you that I got 24 mpg city/33 Highway then you would respond without thinking, “That piece of junk gives such great mileage?"  I would be offended, but complimented simultaneously since I love my old car and feel quite proud that I can keep it running well.  You see, the great job of adaptation which the odometer does allows you the listener to be able to relate to fuel efficiency when it is measured in miles per gallon.  You think in miles, you calculate your trip in miles; you calculate your trip costs in miles. And so the odometer adapts the wheel-revolutions to a figure you can relate to and work with in a meaningful way.

In the adapter pattern, there is an adaptee class.  This class has an interface which clients cannot be expected to work with for one reason or the other.  In most descriptions of the pattern its incompatibility is because the client has already been wired-up to interact with some other interface.  In our scenario, the adaptee is the wheel.  The interface which the wheel exposes is the wheel-turn-rate.  It is the job of the cable/cog-wheels to capture the wheel turn rate and turn it into something readable by humans.  Similarly, in the adapter pattern the adapter inherits the interface which is un-readable and presents an interface which is readable.  In the speedometer example we have one final item and this is the speedometer/odometer display.  This is the public and visible interface which the driver will glance at frantically when he sees the policeman pointing something at him.  He will brake until the speedometer shows him going a speed within the legal limit.  Similarly, in the adapter pattern the Target is the interface which the client will be able to interact with and do useful work through.

The client in the adapter pattern interacts with the target interface.  This is mirrored in the motoring situation where the driver (the client) reads the speedometer (target interface).

Putting it all together, we have an adaptee class which clients do not know how to work with, an adapter class which is configured with the interface of the adaptee and so uses the adaptee’s outputs as its inputs.  Finally, it has a public interface which is useful to the client and so the client interacts with the adapter through public interface it exposes and gets the adapted outputs.

In fact any device or system which performs conversion shows the intent and operation of the adapter pattern.  The adapter pattern could have easily been called the converter pattern.

There is a trick in all of this speed business.  Did you know that changing the size of your tires will change the accuracy of your speedometer and odometer?  This is due to the fact that 50mph always translates to a fixed wheel turn rate.  This is unchangeable.  However, with bigger tires, every turn of your wheel means you are actually covering more distance.  So your odometer, trip-meter, speed and mpg calculations all get thrown off with a changing tire size.  Technically, the fact that your tires experience tread-wear means that there is also minute and very gradual variation in all the above measures as time goes by.  This brings up an important point.  In some cases, adapters may need to be calibrated (see the pattern-tweaking section).

In the GoF world, we actually have two major types of adapters.  The class adapter inherits publicly from the target class and privately from the adaptee class.  In C++ you can do multiple inheritances, but not so in VB.NET.  So in VB.NET we publicly implement the interface of the adapter and privately inherit the target class.

In the case of object adapters, we inherit publicly from the target interface while containing an object reference to the adaptee.

Intent seen through O/S & Applications

Ever used Virtual PC on a Macintosh?  This is a program which allows you to install Windows on a Macintosh and pretend that you are using a Wintel setup.  I understand that you can actually install Windows XP directly on Mac OS these days, but a couple years ago Virtual PC was one of the ways you did it.

Anyway, the point is that it allows you to adapt the adaptee’s interface (Mac Hardware’s interface) to the adaptee (the Wintel interface).  You can install Windows XP programs as if you are working with a Wintel, so you are actually exposing the Wintel interface publicly (the target).

Scenario - Sample Code

I know I promised to give sample code which was different from the Real Life illustration, but I like this example so much that I am going to continue using it.

In the example we have a Windows Form representing the dashboard, gas pedal and wheel-turns of a car.  We start with the accelerator pedal which “drives” everything.  Sliding the horizontal scroll bar to the right simulates the pressing of the accelerator.  Behind the scenes and somewhere in imagination-land, there is a consequent increased revving of the engine, which turns the gears and makes the axle turn faster.  Since the wheels are connected to the axle, this should translate into increased ground speed.

The speedometer cable is connected to the axle and therefore spins as fast as the axle.  To get the speed we simply translate the revolutions of the speedo-cable to miles and its turn-rate to mph.

UML – General

Figure 1

UML – Sample Code

Figure 2

Participants – Sample Code

Target–Miles driven

The interface which is useful to the client (driver).  In this case it is a non-existent class whose main member variable is miles-driven.

Adaptee – Axle

Exposes an interface which is not usable by the client (m_WheelTurnRate) and so needs adaptation.

Adapter – Odometer/Speedometer

Converts the interface exposed by the axle into the interface expected by the driver.  In the case of the Speedometer, it converts wheel-turn rate to ground speed in mph.  The odometer in the sample code is an object adapter which accesses the state within the Axle object which it references.  The odometer is a class adapter which inherits from the Axle class and converts the number of wheel turns into distance traveled.

Client – DoDashboardUpdates

Utilizes the converted method or state which the adapter makes available.

Axle (object adapter example)

As discussed earlier, the axle is doing its thing, spinning away and turning the wheels.  So what we get from the axle is just the wheel-turn rate and number of wheel-turns.  It has nothing to do with the pattern in the sense that it has no knowledge of the other participants in the pattern, even though the other participants know about it.  This is typical of adaptees.

Listing 1

Public Class Axle ' Turns the wheels
  Private Shared CarAxle As Axle
 
  Private m_WheelTurnRate As Integer
  Public Shared Function GetAxle() As Axle
  If CarAxle Is Nothing Then
    CarAxle = New Axle
  End If
  Return CarAxle
End Function
 
Public Property WheelTurnRate() As Integer
  Get
  Return m_WheelTurnRate
  End Get
  Set(ByVal Value As Integer)
  m_WheelTurnRate = Value
  End Set
End Property
 
End Class

Sub-Axle (class adapter example)

This is just another incarnation of the axle which we include for convenience in order to demonstrate the class-adapter setup which is utilized by the odometer.

Listing 2

Public Class SubAxle 'Turns the wheels
 
  Friend Sub New()
  End Sub
  Private m_WheelTurnRate As Integer
  Public Property WheelTurnRate() As Integer
    Get
    Return m_WheelTurnRate
    End Get
    Set(ByVal Value As Integer)
    m_WheelTurnRate = Value
    End Set
  End Property
End Class
Odometer and Speedometer

These are the two object adapter classes.  They use object composition/class inheritance by holding a reference to the axle object (the adaptee).  They use their respective adaptation-functions to convert the not-so-useful wheel-turn rate and wheel-turns into ground-speed and miles-driven respectively.  The functions which achieve these adaptations are UpdateMileage and MeasureSpeed respectively.

Listing 3

Public Class Odometer
  Private m_OdometerCable As Axle
  Private m_MilesDriven As Double
  Private PreviousSampleTime As Date
 
Public ReadOnly Property MilesDriven()
 
End Property
 
Public Function UpdateMileage(ByVal WheelTurnsPerMile As Single)
 
  m_MilesDriven + = m_OdometerCable.WheelTurnRate / WheelTurnsPerMile / _
                    3600 * DateDiff(DateInterval.Second, PreviousSampleTime, Now())
  PreviousSampleTime = Now()
End Function
 
End Class
 
 
Public Class Speedometer
 
  Private Shared CarSpeedometer As Speedometer
  Public m_SpeedometerCable As Axle
  Private m_GroundSpeed As Integer
 
  Private Sub New(ByVal AxleConnection)
    m_SpeedometerCable = AxleConnection
  End Sub
 
  Public Function MeasureSpeed(ByVal WheelTurnsPerMile As SingleAs Integer
    m_GroundSpeed = m_SpeedometerCable.WheelTurnRate / WheelTurnsPerMile
    Return m_GroundSpeed
  End Function
 
End Class

MileMeter

This is the class adapter version.  It inherits from the axle class (the adaptee) while implementing Interface MileMeter.  In order to utilize it, we will instantiate it and call the Method which was implemented.  Internally, this method accesses the inherited method/property from the inherited class: Me.WheelTurnRate.  Notice that because WheelTurnRate is inherited, it is internal to the class-adapter and so we do not make a reference looking like: m_OdometerCable.WheelTurnRate.

Listing 4

Public Interface MileMeter
 
Function MeasureMilesDriven(ByVal WheelTurnsPerMile As SingleAs Single
 
  End Interface
 
  Public Class CableReader ' Class adapter
    Inherits SubAxle ' Inherit adaptee
    Implements MileMeter ' Implement target interface
 
    Dim PreviousSampleTime As DateTime
 
    Public Sub New()
      MyBase.New()
      PreviousSampleTime = Now
    End Sub
 
    Public Function MeasureMilesDriven( _
ByVal WheelTurnsPerMile As SingleAs Single Implements MileMeter.MeasureMilesDriven
 
      Dim MileageChange As Single
 
      MileageChange = Me.WheelTurnRate / WheelTurnsPerMile / _
3600 * DateDiff(DateInterval.Second, PreviousSampleTime, Now())
      PreviousSampleTime = Now
 
      Return MileageChange
 
    End Function
 
  End Class

Client

This is the class adapter version.  It inherits from the axle class (the adaptee) while implementing Interface MileMeter.  In order to utilize it, we will instantiate it and call the Method which was implemented.  Internally, this method accesses the inherited method/property from the inherited class: Me.WheelTurnRate.  Notice that because WheelTurnRate is inherited, it is internal to the class-adapter and so we do not make a reference looking like: m_OdometerCable.WheelTurnRate.

Listing 5

Private Sub Form1_Load(_
ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 
  MyAxle = Axle.GetAxle
  WheelOnCar = New Wheel
  WheelTurnsPerMile = 1600 / (WheelOnCar.OverallDiameter * Math.PI)
 
  MyOdometer = Odometer.GetOdometer(MyAxle, LastMileageRecorded)
  MySpeedometer = Speedometer.GetSpeedometer(MyAxle)
 
  TripMeter = New CableReader
 
End Sub
 
Public Sub RenderSpeed(ByVal SpeedToDisplay As Integer)
  Dim LocationOfSpeed As New Point
  Dim AngleInRadians As Single
  Const NeedleLength As Integer = 70
 
  Const XOffset As Integer = 110 ' X coordinate of the point about which the needle rotates
  Const YOffset As Integer = 100 ' Y coordinate of the point about which the needle rotates
  Dim RotationPoint As New Point(XOffset, YOffset)
 
  AngleInRadians = 3 * SpeedToDisplay * Math.PI / 180
 
  LocationOfSpeed.X = -1 * (Math.Cos(AngleInRadians) * NeedleLength) + XOffset
  LocationOfSpeed.Y = -1 * (Math.Sin(AngleInRadians) * NeedleLength) + YOffset
  NeedleGraphics.Clear(Color.FromKnownColor(KnownColor.Control))
  Dim NeedlePen As New Pen(Color.Red, 3)
  NeedleGraphics.DrawLine(NeedlePen, RotationPoint, LocationOfSpeed)
 
End Sub
 
Private Sub vsbGasPedal_Scroll( … ) Handles vsbGasPedal.Scroll
 
  MyAxle.WheelTurnRate = CInt(vsbGasPedal.Value) ' Object Adapter
 
  If MyAxle.WheelTurnRate > 55000 Then
    MyAxle.WheelTurnRate = 55000 ' Speed limited  :-)
  End If
 
   TripMeter.WheelTurnRate = MyAxle.WheelTurnRate ' Class adapter
 
   lblWheelTurnRate.Text = CInt(Me.MyAxle.WheelTurnRate / 3600)
 
  DoDashboardUpdates()
 
End Sub
 
Private Sub Timer1_Tick( … ) Handles Timer1.Tick
  DoDashboardUpdates()
End Sub
 
Public Sub DoDashboardUpdates()
   RenderSpeed(MySpeedometer.MeasureSpeed(WheelTurnsPerMile))
  MyOdometer.UpdateMileage(WheelTurnsPerMile)
 
  lblMilesDriven.Text = Math.Floor(MyOdometer.MilesDriven)
  TenthValueToDisplay = MyOdometer.MilesDriven * 10 Mod 10
  If TenthValueToDisplay = 10 Then
    TenthValueToDisplay = 0
  End If
  lblMilesDriven_Tenth.Text = TenthValueToDisplay
 
  MilesInthisTrip + = TripMeter.MeasureMilesDriven(WheelTurnsPerMile)
  lblMilesDrivenInTrip.Text = Math.Floor(MilesInthisTrip)
 
 
  TenthValueToDisplay = MilesInthisTrip * 10 Mod 10
  If TenthValueToDisplay = 10 Then
    TenthValueToDisplay = 0
  End If
 
  lblTripMiles_Tenths.Text = (MilesInthisTrip * 10) Mod 10
End Sub
End Class
Interface issues

This pattern is all about interfaces.  It is, therefore, hard to summarize the interface issues in two paragraphs.  However, the key point to understand is that the adapter implements the target interface and so exposes methods which are useful to the client.  This is an interacting interface which the clients will access since they should be able to switch seamlessly between interacting with a class, which naturally exposes the target interface and an adaptee which exposes the target interface through the adapter.

Namespace/Scope/Accessor issues

The adaptee should have a private relationship to the adapter, whether it is inherited or contained.  This is because we do not want a client reaching into the internals of the adapter and getting access to the raw, unconverted, untranslated, unadapted state.  Remember that before adaptation, the adaptee’s state is not fit for consumption.  Naturally, since the target’s interface is the interface which is acceptable to the client, then it is the one that is exposed and public.

This is similar to the behavior of a major corporation or political party’s P.R. department or spin-machine.  Things happen day by day in major organizations and it is preferable that the spin-doctors have a go at the material before releasing it to the press or general public.  The “unadorned truth” reflects the adaptee’s interface while the “palatable statements,” which can be released to the press, fits the target interface.  When everything is scoped up properly, the client becomes like the public which reads the press-release in the paper.  When we get the scoping wrong, however, the client becomes like the investigative reporter who has inside contacts and is able to dig mercilessly into the guts of the story and expose all the unpleasant stuff.

Common mistakes in Pattern Literature / Pitfalls to avoid

There is a generally accepted feeling that Adapter is a Post-development strategy for working with a class which was inadequately designed in the past, in order to make it fit the current requirements.  In other words, it helps us to make the best of a bad situation.  I do not agree totally.  Adapter can be designed up front to ensure that you are eventually compatible with the class you really want to work with.  The other issue is that you are in a sense providing some of the work which the expensive toolkit already implements.  But if it provides a level of abstraction that a team of programmers find useful in not only making them more productive, but also providing a standard way of communicating across the project, not to mention removing the necessity of universal recognition-zone-based coding from all their various client codes, then it is well worth it.   

Advanced Issues

Useful Variations

1a        Symmetrical adapters

None of the classes that the client is interested in looks like the target.  They all need to be adapted to look like what we need them to look like.  The speedometer situation is like that since all existing cars (which I know of) translate wheel-turn rates into ground-speed.  However, at some point, the inevitable interventions of big-brother will manifest itself in the use of GPS for speed-measurement (and of course speed-tracking and automatic ticketing).  In this case we will not need the use of an adapter since GPS measures actual distance traveled and not “wheel-turn-rates.”  

1b        Asymmetrical adapters

Some adaptees have the desired interface and so do not need adaptation.  Some adaptees however, need adaptation in order to integrate with the main system, whether from legacy systems or systems newly acquired.

2a        Distributed adapter/Wide adapter

Adapts several methods within the adaptee into several distinct methods which the target interface will utilize.

2b        Solitary adapters

Adapts 1 method in the adaptee to 1 required method in the target.

3a        Extrinsic/Forward adapter

Sucks data out of the adaptee for use in the target.

3b        Intrinsic/Reverse adapter

Passes data from the target to the adaptee in order to update itself.

4ai       Conversion adapter

Converts a single result or value from the adaptee into the equivalent value for use by the target. Similar to Imperial-measurement to Metric-measurement converters or foreign currency converters or wheel-revolutions to distance covered converters (odometer).

4aii      Translating adapter

These adapters perform language and jargon translations, measurement-converters and also currency-rate converters typify the behavior of these adapters.

4b        Amalgamated adapters

Amalgamates several parameters coming out of the adaptee to a single unified value going to the client (such as speedometer converting wheel-turn-numbers and rates into a single speed).

5a      Pre-adaptation

Adapts the adaptee to the target preemptively.  Most pattern-authors do not recognize this as a legitimate form of adapter.  They argue that adapter allows variation in classes after-the-fact in contrast to bridge’s accomodation of variation before-the-fact.

5b      Post-adaptation

The ideal target already exists and the adapter converts subsequent interfaces of adaptees which do not meet the target-interface into that interface.  This is the widely recognized variation.

If you look closely you will realize that pre/post adaptation has bearing on the concept of symmetrical/asymmetrical adaptation.  Arguably, if we know before hand how the interface needs to look but do not have the technology to create that interface, then we will symmetrically do pre-adaptation of all existing systems to the interface which we will target in the future.  That is just one example of a combination of the concepts.

Pattern Tweaking

For some adapters which I refer to as conversion-adapters, we could abstract out the conversion rate into a class of its own so that it becomes an inner-class which we can then maintain more easily and very importantly, more purposely.  By encapsulating this variance, we allow for easier maintenance.  In a code-based illustration of the odometer example, we abstract out the wheel into its own class and also abstract out the wheel-dimensions in a manner which allows us to update it.  This means that when we fit a new tire, the car’s computer will allow us to enter a new tire size.  It would automatically recalibrate the speedometer based on the tire size we fit.  This would ensure that the number of revolutions of the wheel is correctly converted to miles-driven/miles-per-hour.  We could think of this as a useful variation which we call the Calibrated-Adapter. By making the Adapter accessible from outside, we improve maintainability.

Explanation of Pattern subscripts

R        By adapting the unusable interface of the adaptee, we ensure reusability of previously unusable legacy code.

S+      By giving clients a modified external interface to sensitive functionality we can hide certain capabilities that we do not want them to be aware of; the first step to having a secret stolen is knowing that there is one to steal.

Downloads
Summary

In this article you have learned about the usage of Adapter pattern with a help of a sample application.  The application spins the wheel and the article examines the real background behind its real working.


Product Spotlight
Product Spotlight 

©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-12-04 6:23:30 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search