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
{
public interface ITalkable
{
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
{
public sealed class NonEnglishSpeaker
{
public string Name { get; set; }
public int Age { get; set; }
public string FavorFood { get; set; }
public NonEnglishSpeaker()
{
Name = "John";
Age = 4;
FavorFood = "Pasta";
}
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
{
public class AdapterForNonEnglishSpeaker : ITalkable
{
www.askbargains.com.AdapteeClass.NonEnglishSpeaker aNonEnglishSpeakerKid =
new <a href="http://www.askbargains.com.adapteeclass.nonenglishspeaker();/">www.askbargains.com.AdapteeClass.NonEnglishSpeaker();
</a>
#region ITalkable Members
public void TellMeAboutNameInEnglish()
{
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
{
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
{
ITalkable ObjectToTalk { get; set; }
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
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)
{
Teacher Megan = new Teacher();
EnglishSpeakerKid Elizabeth = new EnglishSpeakerKid();
Elizabeth.Name = "Elizabeth";
Elizabeth.Age = 3;
Elizabeth.FavorFood = "Chicken Nuggets";
AdapterForNonEnglishSpeaker John =
new AdapterForNonEnglishSpeaker();
Console.WriteLine
("Miss Megan starts talking to an English Speaker");
Megan.ObjectToTalk = Elizabeth;
Megan.StartChatting();
Console.WriteLine();
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.