Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Adapter Design Pattern

5.00/5 (13 votes)
8 Oct 2009CPOL4 min read 62.1K   588  
This article shows a case study about how we use the Adapter Pattern to Elizabeth's Day Care Center

Background

Again, my favorite place - Elizabeth's daycare center.

In my last article, I talked about how to use the bridge design pattern to solve the communication issue between the teacher and the kids. Like I mentioned, the bridge design pattern will separate the contract and implementation in two independent areas, it gives more flexibility when we focus on the implementation for the new implementer class (example: ITalkable objects in my Bridge pattern article) design. We actually control/maintain all the ITalkable objects as well.

Somehow we could encounter a situation that we have a class completely developed, and we are not allowed to create/modify that class, but we still want to communicate (consume) with it. In that case, we need to have an adapter to enable that class to communicate with us.

In Elizabeth's daycare center case, a teacher (Communicator object who can only understand English) can only communicate to a kid who also speaks English. In order to make the teacher able to talk to all the kids (ITalkable object that doesn't matter what language they speak), we need to have an Adapter to translate their language to English for all the non-English speakers.

For example, in my company, we developed a Log Engine as a framework library that was shared for the company wide applications. In the Log Engine, we currently use the Microsoft Application Block to log all the information to the Windows Events that we could use Event viewer to check all the information. However, we found it'll be a big plus if we could also share all the log information though web, so we create an Adapter class to wrap the Log4net (third party library) to be suitable with our Log Engine to start logging information to the database and display it online.

Introduction

The Adapter Pattern could help us to wrap up an noncommunicable class to become suitable with what it's requested from the consumer class. Adapter Design is very useful for the system integration when some other components have to be adapted by the existing system.

This article introduces an implementation of how we use the adapter pattern to Elizabeth's daycare center.

Adapter Design Pattern Structure

Adapter1.JPG

Class Diagram

AdapterClass.JPG - Click to enlarge image

Implementation Code

AbstractTarget Class

ITalkable

ITalkable is an interface which I use to declare all my Target methods. In other words, all other classes need to inherit from ITalkable interface in order to be able to communicate with all my Communicator classes inside the system .

C#
namespace www.askbargains.com
{
    namespace AdapterDesignPattern
    {
        //Target methods declaration.
        public interface ITalkable
        {
            //In our case study, the following method means that 
	   //Teacher can only talk in English.
            void TellMeAboutNameInEnglish();
            void TellMeAboutAgeInEnglish();
            void TellMeAboutFavorFoodInEnglish();
        }
    }
}

Adaptee Class

NonEnglishSpeaker

I have NonEnglishSpeaker here as an outside component in a separate assembly. It's a sealed class that cannot directly communicate to our System ICommunicator objects since it didn't have our Target operation methods implemented. However, I am going to create an Adapter class to wrap it up so that our system communicator objects can talk to it as an ITalkable object.

C#
using System;

namespace www.askbargains.com
{
    namespace AdapteeClass
    {
        //Seal class (Adaptee) that is going to be used as 
        //an outside system component in our demo
        public sealed class NonEnglishSpeaker
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public string FavorFood { get; set; }

            //constructor assigns the default values for demo purpose. 
            public NonEnglishSpeaker()
            {
                Name = "John";
                Age = 4;
                FavorFood = "Pasta";
            }

            //helper method for demo purpose.
            public void MethodA()
            {
                Console.WriteLine("Hello! I am a Non-English Speaker");
            }
        }
    }
}

Adapter Class

AdapterForNonEnglishSpeaker

AdapterForNonEnglishSpeaker class does the actual implementation for converting our Adaptee (NonEnglishSpeaker) to be ITalkable object. Since our Adaptee(NonEnglishSpeaker) is a sealed class, I can't directly inherit it. Instead creating a Class Adapter, I need to create an Object Adapter to implement all the Target (ITalkable) operation methods.

I declared a local Adaptee (AdapterForNonEnglishSpeaker) instance, and assigned the default value with its properties just for demo purposes. In the Target implementation process, I utilized the NonEnglishSpeaker (aNonEnglishSpeakerKid) object to achieve the converting process.

If the Adaptee class is not sealed, then we can create the Adapter class as the child class of the Adaptee without initializing an Adaptee object for the converting process.

C#
using System;

namespace www.askbargains.com
{  
    namespace AdapterDesignPattern
    {
        //Adapter for Non English Speaker
        public class AdapterForNonEnglishSpeaker : ITalkable
        {
            //Since the NonEnglishSpeaker is sealed, 
            //we cannot directly create a child class for it.
            //we create an instance of NonEnglishSpeaker to be adapted.
            //I use the full name here to clearly show the object is 
            //from outside of the system.
            www.askbargains.com.AdapteeClass.NonEnglishSpeaker aNonEnglishSpeakerKid = 
		new <a href="http://www.askbargains.com.adapteeclass.nonenglishspeaker();/">www.askbargains.com.AdapteeClass.NonEnglishSpeaker();

</a>            //Implement all the ITalkable details to allow the system to communicate.
            #region ITalkable Members

            public void TellMeAboutNameInEnglish()
            {
                //call helper method for client to distinguish between 
	       //an Adapter and Regular ITalkable objects
                aNonEnglishSpeakerKid.MethodA();
                Console.WriteLine("My name is {0}", aNonEnglishSpeakerKid.Name);
            }

            public void TellMeAboutAgeInEnglish()
            {
                aNonEnglishSpeakerKid.MethodA();
                Console.WriteLine("I am {0} years old", 
			aNonEnglishSpeakerKid.Age.ToString());
            }

            public void TellMeAboutFavorFoodInEnglish()
            {
                Console.WriteLine("My favor food is {0}", 
			aNonEnglishSpeakerKid.FavorFood);
            }

            #endregion
        }
    }
}

System Target Class

I borrowed all the following classes from my Bridge Design Pattern article and used them to help my demonstration.

EnglishSpeakerKid

EnglishSpeakerKid class is a regular system class that inherits from the ITalkable target. It implements the Target ITalkable methods and can be directly talked by all the communicators. In this case study,

C#
using System;

namespace www.askbargains.com
{   
    namespace AdapterDesignPattern
    {
         //regular ITalkable class that system can directly talk to.
         public class EnglishSpeakerKid : ITalkable
         {
             public string Name { get; set; }
             public int Age { get; set; }
             public string FavorFood { get; set; }

             #region ITalable Members

             public void TellMeAboutNameInEnglish()
             {
                 Console.WriteLine("My name is {0}", Name);
             }

             public void TellMeAboutAgeInEnglish()
             {
                 Console.WriteLine("I am {0} years old", Age.ToString());
             }

             public void TellMeAboutFavorFoodInEnglish()
             {
                 Console.WriteLine("My favor food is {0}", FavorFood);
             }

             #endregion
         }
    }
}

ICommunicator

An abstract interface that contracts other classes to be a Communicator and gain the ability to communicate to an ITalkable object:

C#
namespace www.askbargains.com
{
    namespace AdapterDesignPattern
    {
        public interface ICommunicator
        {
            //build a bridge between a Communicator and a talkable object (Kid)
            ITalkable ObjectToTalk { get; set; }

            //Start chatting process
            void StartChatting();
        }
    }
}

Teacher

Teacher class will act as a communicator to talk to all the kids:

C#
using System;

namespace www.askbargains.com
{
     namespace AdapterDesignPattern
    {
         public class Teacher : ICommunicator
         {
             public ITalkable ObjectToTalk { get; set; }

             #region ICommunicator Members

             //Implementing the Chatting procedures.
             public void StartChatting()
             {
                 Console.WriteLine("What's your name?");
                 ObjectToTalk.TellMeAboutNameInEnglish();

                 Console.WriteLine("How old are you?");
                 ObjectToTalk.TellMeAboutAgeInEnglish();

                 Console.WriteLine("What's your favor food");
                 ObjectToTalk.TellMeAboutFavorFoodInEnglish();
             }

             #endregion
         }
    }
}

Client App

From the client side, a Teacher (Megan) was created and used to communicate to the kids. We have two kids, Elizabeth and John created as well. Elizabeth can speak English so the teacher can talk to her directly. Unfortunately, John can't speak English, so we use the AdapterForNonEnglishSpeaker to allow the teacher to communicate with John.

Let teacher Megan start talking to the two kids, we will see both English speaker and Non-English speaker are able to answer Megan's questions.

C#
using System;
using www.askbargains.com.AdapterDesignPattern;

namespace www.askbargains.com
{
    namespace Client
    {
        class Program
        {
            static void Main(string[] args)
            {
                //create a teacher
                Teacher Megan = new Teacher();
                
                //Create An English Speaker object
                EnglishSpeakerKid Elizabeth = new EnglishSpeakerKid();
                Elizabeth.Name = "Elizabeth";
                Elizabeth.Age = 3;
                Elizabeth.FavorFood = "Chicken Nuggets";

                //Use Adapter since John can't speak English.
                AdapterForNonEnglishSpeaker John = 
			new AdapterForNonEnglishSpeaker();                

                //teacher Megan starts talking with Elizabeth
                Console.WriteLine
		("Miss Megan starts talking to an English Speaker");
                Megan.ObjectToTalk   = Elizabeth;
                Megan.StartChatting();
                Console.WriteLine();

                //Teacher Megan starts talking with John
                Console.WriteLine
		("Miss Megan starts talking to a Non English Speaker");
                Megan.ObjectToTalk  = John;
                Megan.StartChatting();
                Console.WriteLine();      

                Console.Read();
            }
        }
    }
}

Once we start our client app, you will see that no matter Elizabeth or John, they will give the correct answer to the communicators. Cool!

AdapterOutput.JPG

Conclusion

In this article, I demonstrated how we can use the Adapter Pattern to help Elizabeth's day care to consume an outside component when it's not directly suitable for the system to use. I also use the daycare center for the Bridge Design pattern in my other articles as well. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)