1. 程式人生 > 實用技巧 >結構型模式不相容結構的協調——介面卡模式

結構型模式不相容結構的協調——介面卡模式

完整解決方案

物件介面卡:

Sunny軟體公司開發人員決定使用介面卡模式來重用演算法庫中的演算法,其基本結構如圖所示:

演算法庫重用結構圖

在圖中,ScoreOperation介面充當抽象目標,QuickSort和BinarySearch類充當適配者,OperationAdapter充當介面卡。完整程式碼如下所示:

//抽象成績操作類:目標介面
interface ScoreOperation {
       public int[] sort(int array[]); //成績排序

       public int search(int array[], int key); //成績查詢
}
//快速排序類:適配者
class QuickSort {
        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[] a, int p, int r) { int x = a[r]; int j = p - 1; for (int i = p; i <= r - 1; i++) { if (a[i] <= x) { j++; swap(a, j, i); } } swap(a, j
+ 1, r); return j + 1; } public void swap(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; } }
//二分查詢類:適配者 
class BinarySearch {
        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 
        }
}
//操作介面卡:介面卡
class OperationAdapter implements ScoreOperation {
        private QuickSort sortObj; //定義適配者QuickSort物件 
        private BinarySearch searchObj; //定義適配者BinarySearch物件 

        public OperationAdapter() {
            sortObj = new QuickSort();
            searchObj = new BinarySearch();
        } 138

        public int[] sort(int array[]) {
            return sortObj.quickSort(array); //呼叫適配者類QuickSort的排序方法 

        }

        public int search(int array[], int key) {
            return searchObj.binarySearch(array, key);
            //呼叫適配者類BinarySearch的查詢方法 
        }
}
為了讓系統具備良好的靈活性和可擴充套件性,我們引入了工具類XMLUtil和配置檔案,其中,XMLUtil類的程式碼如下所示:
import javax.xml.parsers.*; 
import org.w3c.dom.*; 
import org.xml.sax.SAXException; 
import java.io.*;
class XMLUtil {
        //該方法用於從XML配置檔案中提取具體類類名,並返回一個例項物件 
        public static Object getBean() {
            try {
                //建立文件物件 
                DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = dFactory.newDocumentBuilder();
                Document doc;
                doc = builder.parse(new File("config.xml"));
                //獲取包含類名的文字節點 
                NodeList nl = doc.getElementsByTagName("className");
                Node classNode = nl.item(0).getFirstChild();
                String cName = classNode.getNodeValue();
                // 通過類名生成例項物件並將其返回 
                Class c = Class.forName(cName);
                Object obj = c.newInstance();
                return obj;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
}
配置檔案config.xml中儲存了介面卡類的類名,程式碼如下所示:
<?xml version="1.0"?> 
<config> 
    <className>OperationAdapter</className> 
</config>
編寫如下客戶端測試程式碼:
 class Client {
        public static void main(String args[]) {
            ScoreOperation operation;
            //針對抽象目標介面程式設計 
            operation = (ScoreOperation) XMLUtil.getBean();
            // 讀取配置檔案,反射生成物件 int scores[] = {84,76,50,69,90,91,88,96}; 
            // 定義成績陣列 int result[]; int score; System.out.println("成績排序結果:"); 
            result = operation.sort(scores);
            // 遍歷輸出成績 
            for (int i : scores) {
                System.out.print(i + ",");
            }
            System.out.println();
            System.out.println("查詢成績90:");
            score = operation.search(result, 90);
            if (score != -1) {
                System.out.println("找到成績90。");
            } else {
                System.out.println("沒有找到成績90。");
            }
            System.out.println("查詢成績92:");
            score = operation.search(result, 92);
            if (score != -1) {
                System.out.println("找到成績92。");
            } else {
                System.out.println("沒有找到成績92。");
            }
        }
}
編譯並執行程式,輸出結果如下:
成績排序結果: 
50,69,76,84,88,90,91,96, 
查詢成績90: 
找到成績90。 
查詢成績92: 
沒有找到成績92。
在本例項中使用了物件介面卡模式,同時引入了配置檔案,將介面卡類的類名儲存在配置檔案中。如果需要使用其他排序演算法類和查詢演算法類,可以增加一個新的介面卡類,使用新的介面卡來適配新的演算法,原有程式碼無須修改。通過引入配置檔案和反射機制,可以在不修改客戶端程式碼的情況下使用新的介面卡,無須修改原始碼,符合“開閉原則”。 類介面卡: 除了物件介面卡模式之外,介面卡模式還有一種形式,那就是類介面卡模式,類介面卡模式和物件介面卡模式最大的區別在於介面卡和適配者之間的關係不同,物件介面卡模式中介面卡和適配者之間是關聯關係,而類介面卡模式中介面卡和適配者是繼承關係,類介面卡模式結構如圖所示: 圖為介面卡模式結構圖 根據類介面卡模式結構圖,介面卡類實現了抽象目標類介面Target,並繼承了適配者類,在介面卡類的request()方法中呼叫所繼承的適配者類的specificRequest()方法,實現了適配。 典型的類介面卡程式碼如下所示:
class Adapter extends Adaptee implements Target {
        public void request() {
            specificRequest();
        }
}
由於Java、C#等語言不支援多重類繼承,因此類介面卡的使用受到很多限制,例如如果目標抽象類Target不是介面,而是一個類,就無法使用類介面卡;此外,如果適配者Adapter為最終(Final)類,也無法使用類介面卡。在Java等面向物件程式語言中,大部分情況下我們使用的是物件介面卡,類介面卡較少使用。 雙向介面卡 : 在物件介面卡的使用過程中,如果在介面卡中同時包含對目標類和適配者類的引用,適配者可以通過它呼叫目標類中的方法,目標類也可以通過它呼叫適配者類中的方法,那麼該介面卡就是一個雙向介面卡,其結構示意圖如圖所示: 上圖雙向介面卡結構示意圖: 雙向介面卡的實現較為複雜,其典型程式碼如下所示:
class Adapter implements Target, Adaptee {
    //同時維持對抽象目標類和適配者的引用
    private Target target;
    private Adaptee adaptee;

    public Adapter(Target target) {
        this.target = target;
    }

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    public void request() {
        adaptee.specificRequest();
    }

    public void specificRequest() {
        target.request();
    }
}
在實際開發中,我們很少使用雙向介面卡。 介面卡模式總結 介面卡模式將現有介面轉化為客戶類所期望的介面,實現了對現有類的複用,它是一種使用頻率非常高的設計模式,在軟體開發中得以廣泛應用,在Spring等開源框架、驅動程式設計(如JDBC中的資料庫驅動程式)中也使用了介面卡模式。 無論是物件介面卡模式還是類介面卡模式都具有如下優點: (1) 將目標類和適配者類解耦,通過引入一個介面卡類來重用現有的適配者類,無須修改原有結構。 (2) 增加了類的透明性和複用性,將具體的業務實現過程封裝在適配者類中,對於客戶端類而言是透明的,而且提高了適配者的複用性,同一個適配者類可以在多個不同的系統中複用。 (3) 靈活性和擴充套件性都非常好,通過使用配置檔案,可以很方便地更換介面卡,也可以在不修改原有程式碼的基礎上增加新的介面卡類,完全符合“開閉原則”。 具體來說,類介面卡模式還有如下優點: 由於介面卡類是適配者類的子類,因此可以在介面卡類中置換一些適配者的方法,使得介面卡的靈活性更強。 物件介面卡模式還有如下優點: (1) 一個物件介面卡可以把多個不同的適配者適配到同一個目標; (2) 可以適配一個適配者的子類,由於介面卡和適配者之間是關聯關係,根據“里氏代換原則”,適配者的子類也可通過該介面卡進行適配。 類介面卡模式的缺點如下: (1) 對於Java、C#等不支援多重類繼承的語言,一次最多隻能適配一個適配者類,不能同時適配多個適配者; (2) 適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類; (3) 在Java、C#等語言中,類介面卡模式中的目標抽象類只能為介面,不能為類,其使用有一定的侷限性。 物件介面卡模式的缺點如下: 與類介面卡模式相比,要在介面卡中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然後再把適配者類的子類當做真正的適配者進行適配,實現過程較為複雜。 適用場景 在以下情況下可以考慮使用介面卡模式: (1) 系統需要使用一些現有的類,而這些類的介面(如方法名)不符合系統的需要,甚至沒有這些類的原始碼。 (2) 想建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。