介面卡模式(adapter pattern)

wavesZh發表於2018-10-15

概念

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。

spring aop類圖

期望獲得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 介面的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用介面卡,而是直接對系統進行重構。

參考文獻

相關文章