擴充卡主要用于接口的轉換或者将接口不相容的類對象組合在一起形成對外統一接口,是一種結構性模式,其本質是是一個中間件,适用于類及其對象。
本文希望通過簡單的介紹和分析,能讓讀者對擴充卡模式有一個簡單直覺的認識和感覺。
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