java設計模式--適配器模式

適配器模式

將一個類的接口轉換成客戶指望的另外一個接口,使得本來的接口不兼容的類能夠一塊兒工做,結構型設計模式.spring

適用場景

適配器模式不是設計階段考慮使用的設計模式,隨着軟件維護因爲接口不相同下的解決方案.設計模式

適配器分類

《Head First 設計模式》中提到適配器分爲兩類:對象適配器,類適配器,此外還有一種==接口適配器==.app

對象適配器:使用組合的方式,假設咱們須要調用的目標是接口,可是現有的類/接口 和 目標接口不兼容,咱們須要一個==適配器==實現目標接口,而且持有 被適配接口/類 對象。框架

類適配器:類適配器在《Head First 設計模式》中原來指多繼承,可是java中,指採用繼承的方式,實現目標接口同時繼承被適配類,這樣繼續使用接口的形式調用適配器,從而無感知達到目的。 可是若是被適配類不可知因素過多,使用起來可能 繼承效果不如組合來得方便,個人理解假如被適配的類都是經過Spring實例化的,那就有點麻煩了.ide

接口適配器:接口適配器區別於前面兩種,假如一個接口很龐大,咱們每次寫一個實現類都須要實現全部方法,而後只改寫咱們用到一兩個方法,那代碼看起來也會很臃腫,因而就有了接口適配器。 使用抽象類實現接口的全部方法,抽象類實現的接口方法所有是空方法,當新須要一個子類時候,繼承抽象類而且只重寫特定的方法便可。源碼分析

簡單案例

​ 前幾天去了香港,住宿一晚發現,香港電壓和內陸是同樣的220V,50HZ,可是咱們不能直接在插頭插上充電線來給手機充電,香港法律規定室內電器插頭都是英式的方腳三柱插頭,內陸的插頭都是兩腳插頭,像手機充電線插頭都是兩個平行的腳,這時候使用酒店提供的轉換器就能夠正常使用充電線了,轉換器就像是適配器。這就是對象適配器的一個小例子,內陸標準的充電插頭規範(雙平行腳)就是咱們的目標接口,可是在香港室內都沒有這種提供使用的雙腳的充電插槽, 轉換器就是適配器,實現了國內插頭標準,由於它有 雙腳插槽,同時它還組合的 三角的充電插槽,無縫銜接.測試

//國內常見插頭接口規範
public interface DoublePin {
    void chargeForDoublePin();
}
//適配器
public class PluginAdaptor implements DoublePin {
    private TripplePin tripplePin;

    public PluginAdaptor(final TripplePin tripplePin) {
        this.tripplePin = tripplePin;
    }

    @Override
    public void chargeForDoublePin() {
        tripplePin.chargeForThreePinOnly();
    }
}
//香港以及之外的插頭規範
public class TripplePin {
    void chargeForThreePinOnly(){
        System.out.println("充電中......");
    }
}

測試類PersonTeststhis

public class PersonTests {
    public static void main(String[] args) {
        TripplePin hotelPlugin = new TripplePin();
        DoublePin myChargeLine=new PluginAdaptor(hotelPlugin);
        myChargeLine.chargeForDoublePin();
    }
}

​ 講解:適配器的目的至關於中間人,中間人確定要知道有兩方信息吧,對象適配器特徵:實現目標接口,持有另一方聯繫;設計

類適配器的例子

public class PluginAdaptor2 extends TripplePin implements DoublePin {

    @Override
    public void chargeForDoublePin() {
        chargeForThreePinOnly();
    }

}

講解:上面適配器的代碼稍做改動就是類適配器的例子, 不一樣之處就是採用繼承的方式做爲中間人,若是說 酒店轉換頭是組合的方式, 那繼承就比如 把方腳三柱插頭和轉換頭 包裹嚴實起來,只留個 雙腳的插槽暴露出來, 咱們可能感知下來:噢,香港和國內充電沒有區別,充電線帶過去同樣使用。

接口適配器的例子

java.awt包中的WindowListener接口定義了幾個 窗口的監聽事件,WindowAdapter實現了接口,可是方法全都是空實現的,若是咱們須要關閉窗口時作額外處理,徹底一個匿名內部類而且重寫windowClosed方法便可,代碼簡潔且效果同樣,(之前寫awt就沒搞明白windowClosing、windowClosed區別是啥)

優勢

提升類的透明性以及複用, 低耦合,有利於程序擴展;符合開閉

Spring中的適配器

​ 如下是第一次讀Spring源碼以後對於適配器模式的理解。Spring中有不少Adapter爲後綴的類,上面幾種形式的適配器很常見對象適配器ConverterAdapter,接口適配器InstantiationAwareBeanPostProcessorAdapterHandlerInterceptorAdapter,固然這些適配器每一個做用不相同須要結合源碼分析,這裏就不記錄了,至於==HandlerAdapter==、==AvisorAdapter==我感受不像是適配器模式,可是是否使用了什麼適配器還不太清楚.

==HandlerAdapter== 理解:名稱叫處理器適配器,在SpringMvc中的做用呢找到那個 HandlerAdapter , 而後就是 處理請求 。 我以爲更像是一種 適配 , 而不是適配器模式 ,也有人 說這更像命令模式,。 前面記錄的適配器模式 是將一個接口轉換爲 另外一個不兼容的接口,供客戶端調用; 先說沒有==HandlerAdapter==的話該是怎麼處理:

​ 獲得mapperHandler.getHandler,以後判斷他是個HttpRequestHandler仍是HandlerMethod等等, 進行大量IF ELSE 的邏輯處理,若是咱們在框架基礎上修改就須要改動源碼,不符合開閉原則。 如今就是將 全部的 HandlerAdapter實現類放到集合中,調support(handler)來判斷是否支持,以後僅僅用這個HandlerAdapter去反射執行控制器的方法;

​ 糾結了很久, 換個角度想 個人目標是調用這個 處理器的方法,咱們假設存在這樣一個接口unamed,這個接口呢用來動態判斷處理器類型而且反射調用;而另外一個不兼容的類就是HandlerAdapter的各類實現類(好比SimpleServletHandlerAdapter 咱們徹底能夠寫個任意名字方法調用 handle.service(request,response); ) ,如今須要將這個unamed接口和各個 不兼容的類實現適配, 我會寫個類去實現 unamed接口,而後實現handle方法, 可是組合的對象變成 HandlerAdapter,而且HandlerAdapter是在每次調用這個接口實現類的handle方法以前去動態初始化.

​ 下面這個看起來更是 適配器模式了吧 , 可是下面的代碼徹底不必,看起來累贅並且徹底不必。

public interface Unamed {
    ModelAndView handle1(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

public class UnamedImpl implements Unamed{
    private HandlerAdapter ha;
    @Override
    public ModelAndView handle1(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
        ha=getHandler();
        return ha.handle(request,response,handler);
    }

     private HandlerAdapter getHandler() {
        //略
         return  null;
     }
 }

FAQ

我好不容易說服了本身HandlerAdapter是適配器模式,若是你能說服我有理有據,歡迎評論告訴我,這塊確實糾結了很久!

相關文章
相關標籤/搜索