設(shè)計(jì)模式的征途—7.適配器(Adapter)模式
在現(xiàn)實(shí)生活中,我們的筆記本電腦的工作電壓大多數(shù)都是20V,而我國(guó)的家庭用電是220V,如何讓20V的筆記本電腦能夠工作在220V的電壓下工作?答案:引入一個(gè)電源適配器,俗稱變壓器,有了這個(gè)電源適配器,生活用電和筆記本電腦即可兼容。
在軟件開發(fā)中,有時(shí)候也會(huì)存在這種不兼容的情況,我們也可以像電源適配器一樣引入一個(gè)稱之為適配器的角色來協(xié)調(diào)這些存在不兼容的結(jié)構(gòu),這種設(shè)計(jì)方案即稱之為適配器模式。
| 適配器模式(Builder) | 學(xué)習(xí)難度:★★☆☆☆ | 使用頻率:★★★★☆ |
一、木有源碼的算法庫(kù)
Background : M公司在很久以前曾經(jīng)開發(fā)了一個(gè)算法庫(kù),里面包含了一些常用的算法,例如排序和查找算法,在進(jìn)行各類軟件開發(fā)時(shí)經(jīng)常需要重用該算法庫(kù)中的算法。在為某學(xué)校開發(fā)教務(wù)管理系統(tǒng)時(shí),開發(fā)人員發(fā)現(xiàn)需要對(duì)學(xué)生成績(jī)進(jìn)行排序和查找。該系統(tǒng)的設(shè)計(jì)人員已經(jīng)開發(fā)了一個(gè)成績(jī)操作接口IScoreOperation,在該接口中聲明了排序方法Sort(int[])和查找方法Search(int[],int)。為了提高排序和查找的效率,開發(fā)人員決定重用算法庫(kù)中的快速排序算法類QuickSort和二分查找算法類BinarySearch。但是,由于某些原因,現(xiàn)在M公司開發(fā)人員已經(jīng)找不到該算法庫(kù)的源代碼,無法直接通過復(fù)制合粘貼操作來重用其中的代碼;部分開發(fā)人員已經(jīng)針對(duì)IScoreOperation接口編寫代碼,如果這時(shí)再要求對(duì)該接口修改或者要求大家直接使用QuickSort類和BinarySearch類將會(huì)導(dǎo)致大量代碼需要修改。
因此,M公司開發(fā)人員面對(duì)這個(gè)沒有遠(yuǎn)嗎的算法庫(kù),遇到了一個(gè)幸福而又煩惱的問題:如何在既不修改現(xiàn)有接口又不需要任何算法庫(kù)代碼的基礎(chǔ)上實(shí)現(xiàn)算法庫(kù)的重用?
通過分析,不難得知,現(xiàn)在M公司面對(duì)的問題有點(diǎn)類似于我們?cè)谧铋_始提到的電壓?jiǎn)栴},成績(jī)操作接口IScoreOperation有點(diǎn)類似于只支持20V電壓的筆記本電腦,而算法庫(kù)好比220V的家庭用電,這兩部分都沒法再進(jìn)行修改,而且它們?cè)臼莾蓚€(gè)完全不相關(guān)的結(jié)構(gòu)。

為了讓IScoreOperation接口與已有算法庫(kù)一起工作,讓它們?cè)谕粋€(gè)系統(tǒng)中能夠兼容,最好的實(shí)現(xiàn)方法是增加一個(gè)類似電源適配器一樣的適配器角色,通過適配器來協(xié)調(diào)這兩個(gè)原本不兼容的結(jié)構(gòu)。
二、適配器模式簡(jiǎn)介
2.1 適配器模式定義
適配器模式的實(shí)現(xiàn)就是把客戶類的請(qǐng)求轉(zhuǎn)化為對(duì)應(yīng)適配者的相應(yīng)接口的調(diào)用。也就是說:當(dāng)客戶類調(diào)用適配器的方法時(shí),在適配器類的內(nèi)部將調(diào)用適配者類的方法,而這個(gè)過程對(duì)于客戶類來說是透明的,客戶類并不直接訪問適配者類。因此,適配器讓那些由于接口不兼容而不能交互的類可以一起工作。
適配器(Adapter)模式:將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口,使接口不兼容的那些類可以一起工作。
2.2 適配器模式主要角色

適配器模式一般包含以下3個(gè)角色:
(1)Target(目標(biāo)抽象類):目標(biāo)抽象類定義了客戶所需要的接口,可以是一個(gè)抽象類或接口,也可以是一個(gè)具體的類。
(2)Adapter(適配器類):適配器可以調(diào)用另一個(gè)接口,作為一個(gè)轉(zhuǎn)換器,對(duì)Adaptee和Target進(jìn)行適配。適配器類是適配者模式的核心,在適配器模式中,它通過繼承Target并關(guān)聯(lián)一個(gè)Adaptee對(duì)象使二者產(chǎn)生聯(lián)系。
(3)Adaptee(適配者類):適配者即被適配的角色,它定義了一個(gè)已經(jīng)存在的接口,這個(gè)接口需要適配,一般是一個(gè)具體類,包含了客戶希望使用的業(yè)務(wù)方法,在某些情況下可能沒有適配者類的源代碼。
三、借助適配器重用算法庫(kù)
3.1 解決方案結(jié)構(gòu)圖

其中,IScoreOpertion接口充當(dāng)抽象目標(biāo),QuickSort和BinarySearch類充當(dāng)適配者,而OperationAdapter充當(dāng)適配器。
3.2 具體實(shí)現(xiàn)
(1)Target(目標(biāo)抽象類):
/// <summary> /// 目標(biāo)接口:抽象成績(jī)操作類 /// </summary> public interface IScoreOperation { // 成績(jī)排序 int[] Sort(int[] array); // 成績(jī)查找 int Search(int[] array, int key); }
(2)Adaptee(適配者類):
/// <summary> /// 適配者A:快速排序類 /// </summary> public class QuickSortHelper { public int[] QuickSort(int[] array) { Sort(array, 0, array.Length - 1); return array; } public void Sort(int[] array, int p, int r) { int q = 0; if (p < r) { q = Partition(array, p, r); Sort(array, p, q - 1); Sort(array, q + 1, r); } } public int Partition(int[] array, int p, int r) { int x = array[r]; int j = p - 1; for (int i = p; i <= r - 1; i++) { if (array[i] <= x) { j++; Swap(array, j, i); } } Swap(array,j+1,r); return j + 1; } public void Swap(int[] array, int i, int j) { int t = array[i]; array[i] = array[j]; array[j] = t; } }
public class BinarySearchHelper { public int BinarySearch(int[] array, int key) { int low = 0; int high = array.Length - 1; while (low <= high) { int mid = (low + high) / 2; int midVal = array[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { return 1; // 找到元素返回1 } } return -1; // 未找到元素返回-1 } }
(3)Adapter(適配器類):
/// <summary> /// 適配器:成績(jī)操作適配器類 /// </summary> public class OperationAdapter : IScoreOperation { private QuickSortHelper sortTarget; private BinarySearchHelper searchTarget; public OperationAdapter() { sortTarget = new QuickSortHelper(); searchTarget = new BinarySearchHelper(); } public int Search(int[] array, int key) { return searchTarget.BinarySearch(array, key); } public int[] Sort(int[] array) { return sortTarget.QuickSort(array); } }
(4)Client 客戶端測(cè)試代碼
public class Client { public static void Main(string[] args) { IScoreOperation operation = (IScoreOperation)AppConfigHelper.GetAdapterInstance(); if (operation == null) { return; } int[] scores = { 84, 76, 50, 69, 90, 91, 88, 96 }; int[] result; int score; Console.WriteLine("測(cè)試成績(jī)排序結(jié)果:"); result = operation.Sort(scores); foreach (int s in result) { Console.Write("{0},", s.ToString()); } Console.WriteLine(); Console.WriteLine("查找是否有90分的人:"); score = operation.Search(scores, 90); if (score == -1) { Console.WriteLine("抱歉,這個(gè)真沒找到~~~"); } else { Console.WriteLine("恭喜,的確存在90分選手~~~"); } Console.WriteLine("查找是否有92分的人:"); score = operation.Search(scores, 92); if (score == -1) { Console.WriteLine("抱歉,這個(gè)真沒找到~~~"); } else { Console.WriteLine("恭喜,的確存在92分選手~~~"); } Console.ReadKey(); } }
為了讓系統(tǒng)具有良好的靈活性和可擴(kuò)展性,引入了配置文件和AppConfigHelper類。
其中,將具體的Adapter實(shí)例配置在配置文件中,如果需要使用其他的排序算法和查找算法類,可以增加一個(gè)新的適配器類,使用新的適配器來適配新的算法,原有代碼無需修改。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="AdapterName" value="Manulife.ChengDu.DesignPattern.Adapter.OperationAdapter, Manulife.ChengDu.DesignPattern.Adapter" /> </appSettings> </configuration>
AppConfigHelper主要用于讀取配置文件并通過反射生成實(shí)例,可以在不修改客戶端代碼地情況下使用新的適配器,其具體代碼如下:
public class AppConfigHelper { public static string GetAdapterName() { string factoryName = null; try { factoryName = System.Configuration.ConfigurationManager.AppSettings["AdapterName"]; } catch (Exception ex) { Console.WriteLine(ex.Message); } return factoryName; } public static object GetAdapterInstance() { string assemblyName = AppConfigHelper.GetAdapterName(); Type type = Type.GetType(assemblyName); var instance = Activator.CreateInstance(type); return instance; } }
編譯并運(yùn)行,結(jié)果如下圖所示:

四、適配器模式小結(jié)
4.1 主要優(yōu)點(diǎn)
(1)將目標(biāo)類和適配者類解耦,從而無須修改原有結(jié)構(gòu)(只需新增一個(gè)適配器類)
(2)增加了類的透明性(適配者類中的業(yè)務(wù)實(shí)現(xiàn)過程)和復(fù)用性(同一個(gè)適配者類可以在多個(gè)不同的系統(tǒng)中復(fù)用)
(3)靈活性和可擴(kuò)展性很好(借助配置文件和反射機(jī)制,可以方便地切換適配器,符合開閉原則)
4.2 應(yīng)用場(chǎng)景
(1)系統(tǒng)需要使用一些現(xiàn)有的類,而這些類的接口(例如方法名)不符合系統(tǒng)的需要,甚至沒有這些類的源碼。
(2)想要?jiǎng)?chuàng)建一個(gè)可以復(fù)用的類,用于一些彼此之間沒有太大關(guān)聯(lián)的類,包括一些可能在將來引進(jìn)的類一起工作。
參考資料

劉偉,《設(shè)計(jì)模式的藝術(shù)—軟件開發(fā)人員內(nèi)功修煉之道》
作者:周旭龍
出處:http://edisonchou.cnblogs.com
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文鏈接。

在現(xiàn)實(shí)生活中,我們的筆記本電腦的工作電壓大多數(shù)都是20V,而我國(guó)的家庭用電是220V,如何讓20V的筆記本電腦能夠工作在220V的電壓下工作?答案:引入一個(gè)電源適配器,俗稱變壓器,有了這個(gè)電源適配器,生活用電和筆記本電腦即可兼容。在軟件開發(fā)中,有時(shí)候也會(huì)存在這種不兼容的情況,我們也可以像電源適配器一樣引入一個(gè)稱之為適配器的角色來協(xié)調(diào)這些存在不兼容的結(jié)構(gòu),這種設(shè)計(jì)方案即稱之為適配器模式。

浙公網(wǎng)安備 33010602011771號(hào)