介面卡主要用於介面的轉換或者將介面不相容的類物件組合在一起形成對外統一介面,是一種結構性模式,其本質是是一箇中介軟體,適用於類及其物件。
本文希望通過簡單的介紹和分析,能讓讀者對介面卡模式有一個簡單直觀的認識和感知。
1.目的
對現有的類的介面進行轉換以符合新的需求。
2.動機
通過轉換或者組合,間接複用已有功能模組完成需求。
3.優缺點
優點:
- 提高了類的複用;
- 組合若干關聯物件形成對外提供統一服務的介面;
- 擴充套件性、靈活性好。
缺點:
- 過多使用適配模式容易造成程式碼功能和邏輯意義的混淆。
- 部分語言對繼承的限制,可能至多隻能適配一個適配者類,而且目標類必須是抽象類。
4.分類
- 類介面卡
- 物件介面卡
- 介面介面卡
本文主要介紹前兩者。
5.主要用途及場景
該模式並不是在設計開發階段考慮的,主要用在想要修改一個已經存在的介面,或者組合若干關聯物件的時候。
- 想用一個已經存在的類,但其介面不符合需求;
- 想建立一個可以複用的類,該類可以與其他不相關的類協同工作;
- 想使用一些已經存在的子類,但是不能對每一個都進行子類化以匹配它們的介面(僅適用於物件Adapter)。物件介面卡可以適配他的父類介面。
6.原理
下面是GoF介紹的典型的類介面卡模式和物件介面卡模式的UML類圖
類介面卡
原理:通過類繼承實現適配,繼承Target的介面,繼承Adaptee的實現
物件介面卡
原理:通過類物件組合實現適配
Target:
定義Client真正需要使用的介面。
Adaptee:
其中定義了一個已經存在的介面,也是我們需要進行適配的介面。
Adapter:
對Adaptee和Target的介面進行適配,保證對target中介面的呼叫可以間接轉換為對Adaptee中介面進行呼叫。
7.實現
接下來先將上面的UML類圖轉換為兩個具體的例子,然後在對每一種型別在使用一個具體例子介紹.
下面我們使用幾個例子來實際體驗一下代理模式的應用。
7.1 類介面卡
定義目標介面類:Target
public interface Target { void request(); }
被適配的類:Adaptee
public class Adaptee { public void adapteeRequest() { System.out.println("adapteeRequest method of Adaptee! "); } }
適配類Adapter,繼承Target的介面request,同時繼承Adaptee的實現adapteeRequest
public class Adapter extends Adaptee implements Target { @Override public void request() { // TODO Auto-generated method stub super.adapteeRequest(); } }
演示:
public class Demo { public static void main(String [] args) { Target target = new Adapter(); target.request(); // result: adapteeRequest method of Adaptee! } }
7.2 物件介面卡
從上面兩張UML圖中可以清楚的看出兩者的區別,物件中Adapter不在繼承Adaptee,而是將Adaptee作為一個資料成員組合到類定義中,從而實現對其介面的訪問。
public class Adapter implements Target { private Adaptee adaptee = new Adaptee(); @Override public void request() { // TODO Auto-generated method stub adaptee.adapteeRequest(); } }
7.3 類介面卡例項——排序
考慮到某系統中有對資料排序的需求,下面使用介面卡看一下如何複用現有的排序介面(下面的例子僅僅為了演示,介面不具實際意義);
現有排序類:EffectiveVectorSort
public class EffectVectorSort { public void vectorSort() { System.out.println("vectorSort method of EffectVectorSort! "); } }
系統排序所需的介面類定義:DataSort
public interface DataSort { void sort(); }
定義介面卡:SortAdapter
public class SortAdapter extends EffectVectorSort implements DataSort { @Override public void sort() { // TODO Auto-generated method stub super.vectorSort(); } }
演示:
public class Demo { public static void main(String [] args) { DataSort dataSort = new SortAdapter(); dataSort.sort(); // vectorSort method of EffectVectorSort! } }
7.4物件介面卡例項——排序
如果系統中不僅有對向量Vector的排序,也有對元組Tuple和連結串列LinkList等高階資料結構的排序,那麼顯然通過無法通過類適配將每一個排序類的子類都繼承。這裡便可以用到物件適配。
需要修改7.3中的SortAdapter,同時需要定義高階資料結構的排序類。
高階資料結構排序類介面:AdvanceDataSort
public interface AdvanceDataSort { void sort(); }
連結串列排序類:LinkListSort
public class LinkListSort implements AdvanceDataSort { @Override public void sort() { // TODO Auto-generated method stub System.out.println("sort method of LinkListSort!"); } }
元組排序類:TupleSort
public class TupleSort implements AdvanceDataSort { @Override public void sort() { // TODO Auto-generated method stub System.out.println("sort method of TupleSort"); } }
重定義介面卡:SortAdapter
public class SortAdapter implements DataSort { private EffectVectorSort vectorSort = new EffectVectorSort(); private AdvanceDataSort listSort = new LinkListSort(); private AdvanceDataSort tupleSort = new TupleSort(); @Override public void sort(String dataType) { // TODO Auto-generated method stub if(dataType == "vector") { vectorSort.vectorSort(); } else if(dataType == "linklist") { listSort.sort(); } else if(dataType == "tuple") { tupleSort.sort(); } else { System.out.println("Invalid Data Type:" + dataType); } } }
演示:
public class Demo { public static void main(String [] args) { DataSort dataSort = new SortAdapter(); dataSort.sort("vector"); // vectorSort method of EffectVectorSort! dataSort.sort("linklist"); // sort method of LinkListSort! dataSort.sort("tuple"); // sort method of TupleSort dataSort.sort("dict"); // Invalid Data Type:dict } }
限於篇幅,先介紹到這裡。上面的例子都是比較直觀,簡單的。好好體會,將其應用到實際程式設計中才是我們的目的。
參考:
GoF《Design Patterns: Elements of Reusable Object-Oriented Software》
https://www.runoob.com/design-pattern/adapter-pattern.html