设计模式—— 适配器模式
Posted answer-geng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式—— 适配器模式相关的知识,希望对你有一定的参考价值。
模式简介
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。
Adpater模式又叫包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
结构说明
Adapter模式一般包含两个版本:类适配器模式和对象适配器模式。类适配器模式通过继承一个类与接口(Java、C#等不支持多重继承的语言),在接口的实现中调用适配者的方法,如下图所示。
对象适配器通过对象的组合,适配器类中包含适配者的实例对象,在接口的实现方法中调用实例对象的方法。
角色说明
- Adapter
适配器,对Adaptee与Target进行适配。
- Adaptee
适配者,提供一个方法,但这个方法与Target接口不兼容,需要适配。
- Target
特定领域相关使用的接口。
类适配器和对象适配器比较
- 类适配器的优点:因为Adapter是Adaptee的子类,所以可以在Adapter中重写Adaptee的方法,使得Adapter更加灵活。
- 对象适配器的优点:一个对象适配器可以把Adaptee和它的子类全都适配到目标接口上。
示例分析
假设我们有一个转换器程序,能够将系统中的数据导出到不同格式的文件,只需要实现IExporter接口,客户端就可以动态调用程序输出不同格式的文件。
interface IExporter
{
void Export();
}
class ExportToExcel : IExporter
{
public void Export()
{
Console.WriteLine("Export to Excel...");
}
}
//客户端调用
static void Main(string[] args)
{
IExporter exporter = new ExportToExcel();
exporter.Export();
Console.ReadLine();
}
公司购买了一个第三方类库PDFWriter,使用这个类库可以轻松地将数据导入到PDF文件中,但是,它只提供了WriteToPDF方法,这与我们的IExporter接口不兼容。
//这里仅仅是为了方便展示示例提供,我们没有第三方类库的源码
class PDFWriter
{
public void WriteToPDF()
{
Console.WriteLine("Write to PDF..");
}
}
由于没有源码,无法进行修改。当然,即使可以修改,也不应该为了实现一个应用去实现特定领域的接口。为了使我们的转换器程序也能够动态调用WriteToPDF方法,添加类PDFAdapter。
class PDFAdapter : IExporter
{
private PDFWriter writer = new PDFWriter();
public void Export()
{
writer.WriteToPDF();
}
}
通过适配器进行动态调用。
static void Main(string[] args)
{
IExporter exporter = new PDFAdapter();
exporter.Export();
Console.ReadLine();
}
双向适配器(Two-way Adapter)
如果适配器同时包含目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么这个适配器就是一个双向适配器,结构示意图如下:
代码如下:
interface IAdaptee
{
void SpecificRequest();
}
class ConcreteAdaptee : IAdaptee
{
public void SpecificRequest()
{
Console.WriteLine("SpecificRequest by ConcreteAdaptee");
}
}
interface ITarget
{
void Request();
}
class ConcreteTarget : ITarget
{
public void Request()
{
Console.WriteLine("Request by ConcreteTarget");
}
}
class Adapter : IAdaptee, ITarget
{
private IAdaptee adaptee;
private ITarget target;
public Adapter(IAdaptee adaptee)
{
this.adaptee = adaptee;
}
public Adapter(ITarget target)
{
this.target = target;
}
public void Request()
{
adaptee.SpecificRequest();
}
public void SpecificRequest()
{
target.Request();
}
}
可插入适配器(Pluggable Adapter)
观察以上介绍的Adapter类,有一个共性,就是只能适配固定的Adaptee(编译阶段就已经确定),不够灵活。而可插入适配器则是可以适配包含不同接口的Adaptee,换句话来说,可插入适配器允许适配在程序运行时动态传入的适配者。下面我们介绍使用Action委托的方式来实现可插入适配器:
首先创建Cat类和Dog类,并且这两个类拥有不同的方法。
class Cat
{
public void Miaow()
{
Console.WriteLine("miao miao miao");
}
}
class Dog
{
public void Bark()
{
Console.WriteLine("wang wang wang");
}
}
创建可插入适配器,由于不确定适配者的类型及接口,只知道将来客户端要调用MakeSound方法。也就是说,适配器本身不知道将来会适配什么样的适配者,也不知道调用适配者的哪个方法,这一切都是由客户端动态传入。
class PluggableAdapter
{
public Action MakeSound { get; private set; }
public PluggableAdapter(Action makeSound)
{
this.MakeSound = makeSound;
}
}
客户端调用,向PluggableAdapter的构造函数传入委托方法,完成适配。
static void Main(string[] args)
{
Dog dog = new Dog();
(new PluggableAdapter(dog.Bark)).MakeSound();
Console.ReadLine();
}
适用场景
使用一个已经存在的类,但它提供的方法与系统中定义的接口不兼容
- 创建一个可以复用的类,用于与一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作
使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它们父类的接口
源码下载
以上是关于设计模式—— 适配器模式的主要内容,如果未能解决你的问题,请参考以下文章