Monday 20 December 2010

Adapter Pattern

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





This is a fairly simple pattern to understand.  The Adapter will usually be most useful when you have to interact with an existing system or with third party code.  In the class diagram above the Client connects to Target which is an abstraction of Adapter. By depending on the abstraction rather than the Adapter itself, the Client is decoupled from implementation changes to the Adapter.  While it would be a bad design and completely against the spirit of the "depend on abstractions, not implementations" design heuristic to omit the Target abstraction and have the Client directly dependent on the Adapter, the Adapter would still be fulfilling its adapting role.  With that in mind, I think the Adapter pattern is unlike most of the other patterns in that the abstraction is not fundamental to the pattern.
A great example of a real life adapter is provided in .NET/COM interoperation, although it is created "under the hood".  For instance if Adaptee is a .NET object and Client is a COM object, the Client has to enlist an adapter in order to be able to call it.  Adapter, therefore, is the COM callable wrapper (CCW).  The Client calls the Adapter's Request() method through the Target COM  interface and the Adapter routes the call to Adaptee's SpecificRequest method().  The reverse scenario is when the Client is a .NET object talking to a COM Adaptee. In this case the .NET object enlists a Runtime Callable Wrapper (RCW) to enable the interoperation.
In my example the adapter (BorderCollie - a very clever dog) enables a Labrador to understand an EnglishPointer's bark. The BorderCollie converts the EnglishPointer's enumerated BarkType to the Labrador's (incompatible) one.
using System;
 
namespace EnglishPointer
{
    using BorderCollie;
 
    public class EnglishPointerDog
    {
        public enum BarkType
        {
            Loud,
            Grating
        }
 
        static void Main(string[] args)
        {
            Bark(BarkType.Loud);
            Console.ReadLine();
        }
 
        static void Bark(BarkType barkType)
        {
            IBarkAdapter jess = new BorderCollieDog();
            jess.InterpretBark(barkType);    
        }
    }
}
 
namespace Labrador
{
    // The class to be adapted
    public class LabradorDog
    {
        public enum BarkType
        {
            Deep,
            Whining
        }
 
        public void ListenToBark(BarkType labradorBark)
        {
            Console.WriteLine(Enum.GetName
            (typeof (BarkType), labradorBark));
        }
    }
}


namespace BorderCollie
{
    using EnglishPointer;
    using Labrador;

    public interface IBarkAdapter
    {
        void InterpretBark(EnglishPointerDog.BarkType pointerBark);
    }


    // The class that does the adapting
    public class BorderCollieDog : IBarkAdapter
    {
        public void InterpretBark
        (EnglishPointerDog.BarkType pointerBark)
        {
            LabradorDog royston = new LabradorDog();


            if (pointerBark == EnglishPointerDog.BarkType.Loud)
            {
                royston.ListenToBark(LabradorDog.BarkType.Deep);
            }
            else
            {
                royston.ListenToBark(LabradorDog.BarkType.Whining);
            }      
        }
    } 
}

No comments:

Post a Comment