1. 程式人生 > >設計模式的征途—7.介面卡(Adapter)模式

設計模式的征途—7.介面卡(Adapter)模式

在現實生活中,我們的膝上型電腦的工作電壓大多數都是20V,而我國的家庭用電是220V,如何讓20V的膝上型電腦能夠工作在220V的電壓下工作?答案:引入一個電源介面卡,俗稱變壓器,有了這個電源介面卡,生活用電和膝上型電腦即可相容。

在軟體開發中,有時候也會存在這種不相容的情況,我們也可以像電源介面卡一樣引入一個稱之為介面卡的角色來協調這些存在不相容的結構,這種設計方案即稱之為介面卡模式。

介面卡模式(Builder) 學習難度:★★☆☆☆ 使用頻率:★★★★☆

一、木有原始碼的演算法庫

Background : M公司在很久以前曾經開發了一個演算法庫,裡面包含了一些常用的演算法,例如排序和查詢演算法,在進行各類軟體開發時經常需要重用該演算法庫中的演算法。在為某學校開發教務管理系統時,開發人員發現需要對學生成績進行排序和查詢。該系統的設計人員已經開發了一個成績操作介面IScoreOperation,在該介面中聲明瞭排序方法Sort(int[])和查詢方法Search(int[],int)。為了提高排序和查詢的效率,開發人員決定重用演算法庫中的快速排序演算法類QuickSort和二分查詢演算法類BinarySearch。但是,由於某些原因,現在M公司開發人員已經找不到該演算法庫的原始碼,無法直接通過複製合貼上操作來重用其中的程式碼;部分開發人員已經針對IScoreOperation介面編寫程式碼,如果這時再要求對該介面修改或者要求大家直接使用QuickSort類和BinarySearch類將會導致大量程式碼需要修改。

因此,M公司開發人員面對這個沒有遠嗎的演算法庫,遇到了一個幸福而又煩惱的問題:如何在既不修改現有介面又不需要任何演算法庫程式碼的基礎上實現演算法庫的重用?  

  通過分析,不難得知,現在M公司面對的問題有點類似於我們在最開始提到的電壓問題,成績操作介面IScoreOperation有點類似於只支援20V電壓的膝上型電腦,而演算法庫好比220V的家庭用電,這兩部分都沒法再進行修改,而且它們原本是兩個完全不相關的結構。

  為了讓IScoreOperation介面與已有演算法庫一起工作,讓它們在同一個系統中能夠相容,最好的實現方法是增加一個類似電源介面卡一樣的介面卡角色,通過介面卡來協調這兩個原本不相容的結構。

二、介面卡模式簡介

2.1 介面卡模式定義

  介面卡模式的實現就是把客戶類的請求轉化為對應適配者的相應介面的呼叫。也就是說:當客戶類呼叫介面卡的方法時,在介面卡類的內部將呼叫適配者類的方法,而這個過程對於客戶類來說是透明的,客戶類並不直接訪問適配者類。因此,介面卡讓那些由於介面不相容而不能互動的類可以一起工作。

介面卡(Adapter)模式:將一個介面轉換成客戶希望的另一個介面,使介面不相容的那些類可以一起工作。

2.2 介面卡模式主要角色

  介面卡模式一般包含以下3個角色:

  (1)Target(目標抽象類):目標抽象類定義了客戶所需要的介面,可以是一個抽象類或介面,也可以是一個具體的類。

  (2)Adapter(介面卡類):介面卡可以呼叫另一個介面,作為一個轉換器,對Adaptee和Target進行適配。介面卡類是適配者模式的核心,在介面卡模式中,它通過繼承Target並關聯一個Adaptee物件使二者產生聯絡。

  (3)Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的介面,這個介面需要適配,一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的原始碼。

三、藉助介面卡重用演算法庫

3.1 解決方案結構圖

  其中,IScoreOpertion介面充當抽象目標,QuickSort和BinarySearch類充當適配者,而OperationAdapter充當介面卡。

3.2 具體實現

  (1)Target(目標抽象類):

    /// <summary>
    /// 目標介面:抽象成績操作類
    /// </summary>
    public interface IScoreOperation
    {
        // 成績排序
        int[] Sort(int[] array);
        // 成績查詢
        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>
    /// 介面卡:成績操作介面卡類
    /// </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 客戶端測試程式碼

    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("測試成績排序結果:");
            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("抱歉,這個真沒找到~~~");
            }
            else
            {
                Console.WriteLine("恭喜,的確存在90分選手~~~");
            }

            Console.WriteLine("查詢是否有92分的人:");
            score = operation.Search(scores, 92);
            if (score == -1)
            {
                Console.WriteLine("抱歉,這個真沒找到~~~");
            }
            else
            {
                Console.WriteLine("恭喜,的確存在92分選手~~~");
            }

            Console.ReadKey();
        }
    }

  為了讓系統具有良好的靈活性和可擴充套件性,引入了配置檔案和AppConfigHelper類。

  其中,將具體的Adapter例項配置在配置檔案中,如果需要使用其他的排序演算法和查詢演算法類,可以增加一個新的介面卡類,使用新的介面卡來適配新的演算法,原有程式碼無需修改。

<?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主要用於讀取配置檔案並通過反射生成例項,可以在不修改客戶端程式碼地情況下使用新的介面卡,其具體程式碼如下:

    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;
        }
    }
View Code

  編譯並執行,結果如下圖所示:

  

四、介面卡模式小結

4.1 主要優點

  (1)將目標類和適配者類解耦,從而無須修改原有結構(只需新增一個介面卡類)

  (2)增加了類的透明性(適配者類中的業務實現過程)和複用性(同一個適配者類可以在多個不同的系統中複用)

  (3)靈活性和可擴充套件性很好(藉助配置檔案和反射機制,可以方便地切換介面卡,符合開閉原則)

4.2 應用場景

  (1)系統需要使用一些現有的類,而這些類的介面(例如方法名)不符合系統的需要,甚至沒有這些類的原始碼。

  (2)想要建立一個可以複用的類,用於一些彼此之間沒有太大關聯的類,包括一些可能在將來引進的類一起工作。

參考資料

      DesignPattern

  劉偉,《設計模式的藝術—軟體開發人員內功修煉之道》

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

設計模式征途7.介面卡Adapter模式

在現實生活中,我們的膝上型電腦的工作電壓大多數都是20V,而我國的家庭用電是220V,如何讓20V的膝上型電腦能夠工作在220V的電壓下工作?答案:引入一個電源介面卡,俗稱變壓器,有了這個電源介面卡,生活用電和膝上型電腦即可相容。 在軟體開發中,有時候也會存在這種不相容的情況,我們也可以像電源介面卡一樣引入

設計模式——介面卡Adapter模式

概述   什麼是介面卡?在我們生活中的介面卡比如插頭轉換器(中標轉美標)、USB介面轉換器(type-c轉蘋果),電腦電源介面卡(交流電轉低電壓直流)等。像這種將兩者有差異的東西通過介面卡使他們成為相互適合的東西。在程式世界中,經常存在現有的程式無法直接使用,需要做適當的變換後才能使用的情況,這種

C++設計模式介面卡Adapter模式

1. 標準定義介面卡模式標準定義:將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 2. 分析和說明介面卡模式屬於結構型設計模式。適用性:你想使用一個已經存在的類,而它的介面不符合你的需求。你想建立一個

介面卡Adapter模式23

轉載自:https://www.cnblogs.com/promise-7/archive/2012/05/16/2503596.html 一、 介面卡(Adapter)模式 介面卡模式把一個類的介面變換成客戶端所期待的另一種介面,從而使原本介面不匹配而無法在一起工作的兩個類能夠在

設計模式8—— 結構型 ——介面卡Adapter

定義:將一個類的介面轉換成客戶期望的另一個介面,使原本介面不相容的類可以一起工作 型別:結構型 適用場景: 已經存在的類,它的方法和需求不匹配時(方法結果系統或者相似) 不是軟體設計階段考慮的設計模式,是隨著軟體維護,

設計模式征途—5.原型Prototype模式

pla width 共享 太多的 isp text 一模一樣 軟件 集合 相信大多數的人都看過《西遊記》,對孫悟空拔毛變出小猴子的故事情節應該都很熟悉。孫悟空可以用猴毛根據自己的形象復制出很多跟自己一模一樣的小猴兵出來,其實在設計模式中也有一個類似的模式,我們可以通過一個原

設計模式征途—16.訪問者Visitor模式

lose mar rtm image 3.1 conf 系統 .get 封裝性 在患者就醫時,醫生會根據病情開具處方單,很多醫院都會存在以下這個流程:劃價人員拿到處方單之後根據藥品名稱和數量計算總價,而藥房工作人員根據藥品名稱和數量準備藥品,如下圖所示。 在軟件開發中

設計模式征途—18.策略Strategy模式

滿足 應用 基礎 blog title pla 生成 display 多個 俗話說條條大路通羅馬,很多情況下實現某個目標地途徑都不只一條。在軟件開發中,也會時常遇到這樣的情況,實現某一個功能有多條途徑,每一條途徑都對應一種算法。此時,可以使用一種設計模式來實現靈活地選擇解決

設計模式征途—20.備忘錄Memento模式

行為 修煉之道 mda 3.2 ima 位置 pri 捕獲 spl 相信每個人都有後悔的時候,但是人生並無後悔藥,有些錯誤一旦發生就無法再挽回,有些事一旦錯過就不會再重來,有些話一旦說出口也就不可能再收回,這就是人生。為了不讓自己後悔,我們總是需要三思而後行。這裏我們要學習

設計模式征途—10.裝飾Decorator模式

雖然目前房價依舊很高,就連我所在的成都郊區(非中心城區)的房價均價都早已破萬,但卻還是阻擋不了大家對新房的渴望和買房的熱情。如果大家買的是清水房,那麼無疑還有一項艱鉅的任務在等著大家,那就是裝修。對新房的裝修並沒有改變房屋用於居住的本質,但它可以讓房子變得更加漂亮和溫馨以及更加實用。在軟體設計中,也有一種類似

設計模式征途—11.外觀Facade模式

在軟體開發中,有時候為了完成一項較為複雜的功能,一個類需要和多個其他業務類互動,而這些需要互動的業務類經常會作為一個完整的整體出現,由於涉及的類比較多,導致使用時程式碼較為複雜,此時,特別需要一個類似服務員一樣的角色,由他來負責和多個業務類進行互動,而使用這些業務類的類只需要和該類進行互動即可。外觀模式通過引

設計模式征途—13.代理Proxy模式

所謂代購,簡單說來就是找人幫忙購買所需要的商品。代購分為兩種型別,一種是因為在當地買不到某件商品,又或者是因為當地這件商品的價格比其他地區的貴,因此託人在其他地區甚至國外購買該商品,然後通過快遞發貨或直接攜帶回來。另一種則是消費者對想要購買的商品相關資訊的缺乏,自己無法確定其實際價值,因此只好委託中介講價或購

設計模式征途—19.命令Command模式

在生活中,我們裝修新房的最後幾道工序之一是安裝插座和開關,通過開關可以控制一些電器的開啟和關閉,例如電燈或換氣扇。在購買開關時,使用者並不知道它將來到底用於控制什麼電器,也就是說,開關與電燈、換氣扇並無直接關係,一個開關在安裝之後可能用來控制電燈,也可能用來控制換氣扇或者其他電器裝置。相同的開關可以通過不同的

設計模式征途—23.直譯器Interpreter模式

雖然目前計算機程式語言有好幾百種,但有時人們還是希望用一些簡單的語言來實現特定的操作,只需要向計算機輸入一個句子或檔案,就能按照預定的文法規則來對句子或檔案進行解釋。例如,我們想要只輸入一個加法/減法表示式,它就能夠計算出表示式結果。例如輸入“1+2+3-4+1”時,將輸出計算結果為3。像C++,Java或C

設計模式:裝飾器Decorator模式

讓我 分享圖片 底部 .com 一件事 輸出 PE 新的 int 設計模式:裝飾器(Decorator)模式 一、前言 裝飾器模式也是一種非常重要的模式,在Java以及程序設計中占據著重要的地位。比如Java的數據流處理,我們可能看到數據流經過不同的類的包裝和包裹,最

設計模式:觀察者Observer模式

image 強制轉換 trace vat PE sta obs observer -a 設計模式:觀察者(Observer)模式 一、前言 觀察者模式其實最好的名稱應該是“發布訂閱”模式,和我們現在大數據之中的發布訂閱方式比較類似,但是也有區別的地方,在上一個設計模式,

設計模式:享元FlyWeight模式

例子 清理 什麽 public == lean http 變量 -- 設計模式:享元(FlyWeight)模式 一、前言 享元(FlyWeight)模式顧名思義,既是輕量級的,原因就是享元,共享元素,這裏的元素指的是對象。如何共享對象,那就是在檢測對象產生的時候,如

Koffee設計模式學習之路 —— 模式學習總結思路

    這篇部落格沒有相關技術細節,僅作為自己對設計模式這個東西的一點感悟和以後設計模式系列部落格的一個寫作思路。     作為非科班出身,誤打誤撞進入程式設計的人,在上研究生期間對於程式的唯一要求就是:能用。彼時,不知道有面向物件,記憶體管理,多執行緒,

Kotlin:編寫Android介面卡Adapter

說好今天要寫一個使用Kotlin寫Adapter的列子,我想了半天也沒有組織好語言,直接上程式碼吧,有一定Android基礎的小夥伴肯定是能看的懂的 package com.example.administrator.kotlintest import

設計模式之裝飾者Decorator模式

首先來看一個場景,如圖: 工人分為很多種類,比如電工,管道工等等,同時又有A公司的電工,B公司的電工,A公司的管道工,B公司的管道工等等,那麼當有M個工種和N個公司的時候,就會有 M * N 個子類,這個繼承體系就會變得很龐大和複雜。那麼如何簡化呢,那麼