Adapter Design Pattern





5.00/5 (13 votes)
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
Class Diagram
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 .
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.
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.
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 www.askbargains.com.AdapteeClass.NonEnglishSpeaker();
//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,
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:
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:
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.
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!
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.