适配器设计模式





5.00/5 (13投票s)
本文展示了一个关于如何将适配器模式应用于Elizabeth日托中心的案例研究。
背景
再次来到我最喜欢的地方 - 伊丽莎白的日托中心。
在我上一篇文章中,我讨论了如何使用桥接设计模式来解决老师和孩子们之间的通信问题。正如我所提到的,桥接设计模式会将契约和实现分离到两个独立的区域,这为我们关注新的实现者类(例如,我桥接模式文章中的ITalkable
对象)的设计提供了更大的灵活性。我们实际上也控制/维护所有ITalkable
对象。
有时我们可能会遇到这种情况:有一个已经完全开发的类,我们不允许创建/修改它,但我们仍然想与之通信(使用)。在这种情况下,我们需要一个适配器来使该类能够与我们通信。
在伊丽莎白日托中心的案例中,一位老师(只能听懂英语的Communicator
对象)只能与同样说英语的孩子交流。为了让老师能够与所有孩子(ITalkable
对象,无论他们说什么语言)交谈,我们需要一个适配器来将他们的语言翻译成英语,以便所有非英语使用者都能听懂。
例如,在我公司,我们开发了一个日志引擎作为框架库,供公司所有应用程序共享。在日志引擎中,我们目前使用Microsoft Application Block将所有信息记录到Windows事件日志中,我们可以使用事件查看器来检查所有信息。然而,我们发现如果能通过Web共享所有日志信息将会是一大优势,因此我们创建了一个Adapter
类来包装Log4net(第三方库),使其适合我们的日志引擎,以便开始将信息记录到数据库并在网上显示。
引言
适配器模式可以帮助我们包装一个无法通信的类,使其适合消费者类的要求。适配器设计对于系统集成非常有用,因为某些其他组件需要被现有系统适配。
本文介绍了一个在伊丽莎白日托中心使用适配器模式的实现。
适配器设计模式结构

类图

实现代码
抽象目标类
ITalkable
ITalkable
是一个接口,我用它来声明我所有的Target
方法。换句话说,所有其他类都需要继承ITalkable
接口,才能与我系统中的所有Communicator
类进行通信。
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();
}
}
}
被适配者类
NonEnglishSpeaker
我在这里将NonEnglishSpeaker
作为一个外部组件,放在一个单独的程序集中。它是一个密封类,无法直接与我们的System ICommunicator
对象通信,因为它没有实现我们的目标操作方法。但是,我将创建一个Adapter
类来包装它,以便我们的系统通信器对象可以将其视为ITalkable
对象进行通信。
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");
}
}
}
}
适配器类
AdapterForNonEnglishSpeaker
AdapterForNonEnglishSpeaker
类实现了将我们的Adaptee
(NonEnglishSpeaker
)转换为ITalkable
对象的实际操作。由于我们的Adaptee
(NonEnglishSpeaker
)是一个密封类,我不能直接继承它。而不是创建类适配器,我需要创建一个对象适配器来实现所有Target
(ITalkable
)操作方法。
我声明了一个本地的Adaptee
(AdapterForNonEnglishSpeaker
)实例,并为其属性分配了默认值,仅用于演示目的。在目标实现过程中,我利用了NonEnglishSpeaker
(aNonEnglishSpeakerKid
)对象来实现转换过程。
如果Adaptee
类不是密封的,那么我们可以创建Adapter
类作为Adaptee
的子类,而无需初始化Adaptee
对象来进行转换过程。
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
}
}
}
系统目标类
我从我的桥接设计模式文章中借用了以下所有类,并用它们来帮助我的演示。
EnglishSpeakerKid
EnglishSpeakerKid
类是一个普通的系统类,继承自ITalkable
目标。它实现了Target ITalkable
方法,并且可以被所有通信器直接对话。在此案例研究中,
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
一个抽象接口,它要求其他类成为Communicator
并获得与ITalkable
对象通信的能力。
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
类将充当通信器,与所有孩子交谈。
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
}
}
}
客户端应用程序
从客户端方面,创建了一个Teacher
(Megan
)对象,并用于与孩子们交流。我们还创建了两个孩子,Elizabeth
和John
。Elizabeth
会说English
,所以老师可以直接和她说话。不幸的是,John
不会说English
,所以我们使用AdapterForNonEnglishSpeaker
来让老师和John
交流。
让老师Megan
开始和两个孩子说话,我们会看到会说English
的孩子和不会说English
的孩子都能回答Megan
的问题。很酷!
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();
}
}
}
}
当我们启动客户端应用程序时,您会看到,无论是Elizabeth
还是John
,他们都会给通信器正确的答复。太棒了!

结论
在本文中,我演示了如何使用适配器模式帮助伊丽莎白日托中心使用一个外部组件,当它不直接适合系统使用时。我还使用了日托中心作为我其他文章中桥接设计模式的示例。