設計模式(七)—— 適配器模式
模式簡介
將一個類的接口轉換成客戶希望的另外一個接口。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();
}
適用場景
使用一個已經存在的類,但它提供的方法與系統中定義的接口不兼容
- 創建一個可以復用的類,用於與一些彼此之間沒有太大關聯的類,包括一些可能在將來引進的類一起工作
使用一些已經存在的子類,但是不可能對每一個都進行子類化以匹配它們的接口。對象適配器可以適配它們父類的接口
源碼下載
dotnet-design-pattern_adapter
設計模式(七)—— 適配器模式