概念
wiki: 有時候也稱包裝樣式或者包裝(wrapper)。將一個類的介面轉接成使用者所期待的。一個適配使得因介面不相容而不能在一起工作的類能在一起工作,做法是將類自己的介面包裹在一個已存在的類中。
實現程式碼
原有功能類
class Adaptee {
public void methodB() {
}
}
複製程式碼
期待功能介面
interface Target {
pubic void doWork();
}
複製程式碼
介面卡類
class Adapter implements Target {
private Adaptee adaptee;
publiic Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void doWork() {
adapee.methodB();
}
}
複製程式碼
開源專案中的應用
JDK中java.util.Arrays#asList()
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
複製程式碼
Spring中AdvisorAdapter
Advice(通知)的型別有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每個型別 Advice(通知)都有對應的攔截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 需要將每個 Advice(通知)都封裝成對應的攔截器型別,返回給容器,所以需要使用介面卡模式對 Advice 進行轉換。由於Advisor鏈需要的是MethodInterceptor ,所以每個Advisor中Advice需要適配成對應的MethodInterceptor。
期望獲得MethodInterceptor,但是MethodBeforeAdvice無法實現。
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, Object target) throws Throwable;
}
複製程式碼
建立Adapter進行適配,通過getInterceptor來獲取期望的MethodInterceptor。
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
// 期望獲取一個MethodInterceptor,但是原有介面無法實現,
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
複製程式碼
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
/**
* 註冊介面卡
*/
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// 使用介面卡
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}
@Override
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
....
}
複製程式碼
使用場景
在軟體開發中,也就是系統的資料和行為都正確,但介面不相符時,我們應該考慮用介面卡,目的是使控制範圍之外的一個原有物件與某個介面匹配。介面卡模式主要應用於希望複用一些現存的類,但是介面又與複用環境要求不一致的情況。比如在需要對早期程式碼複用一些功能等應用上很有實際價值。適用場景大致包含三類:
1、已經存在的類的介面不符合我們的需求;
2、建立一個可以複用的類,使得該類可以與其他不相關的類或不可預見的類(即那些介面可能不一定相容的類)協同工作;
3、在不對每一個都進行子類化以匹配它們的介面的情況下,使用一些已經存在的子類。
優缺點
- 優點
介面卡模式也是一種包裝模式,它與裝飾模式同樣具有包裝的功能,此外,物件介面卡模式還具有委託的意思。總的來說,介面卡模式屬於補償模式,專用來在系統後期擴充套件、修改時使用。
- 缺點
過多的使用介面卡,會讓系統非常零亂,不易整體進行把握。比如,明明看到呼叫的是 A 介面,其實內部被適配成了 B 介面的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用介面卡,而是直接對系統進行重構。