設計模式 | 適配器模式

寫在前面:

適配器模式(Adapter Pattern):將一個接口轉換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名爲包裝器(Wrapper)。適配器模式既可以作爲類結構型模式,也可以作爲對象結構型模式。 在適配器模式中,我們通過增加一個新的適配器類來解決接口不兼容的問題,使得原本沒有任何關係的類可以協同工作。

根據適配器類與適配者類的關係不同,適配器模式可分爲對象適配器和類適配器兩種,在對象適配器模式中,適配器與適配者之間是關聯關係;在類適配器模式中,適配器與適配者之間是繼承(或實現)關係。

角色

Target(目標抽象類):目標抽象類定義客戶所需接口,可以是一個抽象類或接口,也可以是具體類。

Adapter(適配器類):適配器可以調用另一個接口,作爲一個轉換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心,在對象適配器中,它通過繼承Target並關聯一個Adaptee對象使二者產生聯繫。

Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的接口,這個接口需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的源代碼。

 

案例背景

我們國家的民用電都是 220V,日本是 110V,而我們的手機充電一般需要 5V,這時候要充電,就需要一個電壓適配器,將 220V 或者 100V 的輸入電壓變換爲 5V 輸出。

定義輸出電壓的產品:

AC.java

public interface AC {
    int output();
}

產品接口產生中國和日本的兩種子產品

ACChina.java

public class ACChina implements AC {

    @Override
    public int output() {
        return 220;
    }
}

ACJapan.java

public class ACJapan implements AC {
    @Override
    public int output() {
        return 110;
    }
}

由於適配器只能適應一種產品,也就是說 中國和日本的兩種電壓 需要兩個適配器來解決,so,又產生了電壓適配器產品。

ACAdapter.java

public interface ACAdapter {
    boolean support(AC ac); //是否支持此種電壓適配

    int outputDC5V(AC ac); //適配操作
}
中國適配電壓:ChinaACAdapter.java
public class ChinaACAdapter implements ACAdapter {
    @Override
    public boolean support(AC ac) {
        return ac.output() == 220;
    }

    @Override
    public int outputDC5V(AC ac) {
        return ac.output()/44;
    }
}

日本適配電壓:JapanACAdapter.java

public class JapanACAdapter implements ACAdapter {
    @Override
    public boolean support(AC ac) {
        return ac.output() == 110;
    }

    @Override
    public int outputDC5V(AC ac) {
        return ac.output()/22;
    }
}

適配操作:

@Test
public void tt(){

       ACAdapter acAdapter = new ChinaACAdapter();
       ACChina acChina = new ACChina();
       if(acAdapter.support(acChina)){
           System.out.println(acAdapter.outputDC5V(acChina));
       }

}

success。

總結:

中國的電壓使用中國的適配器,產生5V交流電。

 

接下來,結合Spring AOP,看看適配器模式的實際運用。

切面編程(也可叫通知)大致分爲 前置通知(BeforeAdvice),後置通知(AfterAdvice),環繞通知(ThrowsAdvice)。

這些通知的最底層產品是Advice。我們着重介紹介紹前置通知,其他的原理相同。

Advice.java

public interface Advice {
}

BeforeAdvice.java

public interface BeforeAdvice extends Advice {
}

MethodBeforeAdvice.java

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
}

注意:在MethodBeforeAdvice裏面就定義了前置通知的方法及參數,需要利用反射區調用。

 

接下來就到了 MethodBeforeAdvice的實現類也就是前置通知的具體操作了。

AspectJMethodBeforeAdvice.java
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
    public AspectJMethodBeforeAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }

    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        this.invokeAdviceMethod(this.getJoinPointMatch(), (Object)null, (Throwable)null);
    }

    public boolean isBeforeAdvice() {
        return true;
    }

    public boolean isAfterAdvice() {
        return false;
    }
}

## 不知道你們注意到沒有,他的判斷方法包括 isBeforeAdvice 和 isAfterAdvice。

再然後,就是前置通知適配器了,因爲要有一個適配器去適配攔截,然後再方法執行前,做一個通知。

MethodBeforeAdviceInterceptor.java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
    private final MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }
}

沒錯,就是一個攔截器,自定義註解瞭解一波,一般的註解都要利用反射+攔截器或者過濾器去做。可見,spring的做法是攔截器。當攔截到方法的上面有@before的時候,自然會調用我們這個前置通知適配器。他們幫我們做一些前置通知,以及目標方法的invoke()。然後返回結果。所以我理解的spring aop 就是 動態代理技術的使用。而動態代理的使用依靠的就是 反射與適配器模式,這裏面說的可能過於絕對。但是,你懂的 就好啦。日後再聊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章