透過源碼學習設計模式7-適配器模式與HandlerApapter

  • 2019 年 10 月 10 日
  • 筆記

定義

適配器模式把一個類的介面,變換成客戶端所期待的另一種介面,使原本因介面不匹配的兩個類能夠在一起工作。

結構

角色:

Client:用戶類,使用新介面Target來完成某些特定的需求。

Target:新的介面類,開放特定介面request來完成某些特定操作,與Client協作。

Adaptee:原有的類,即需要適配的類或被適配者類。

Adapter:適配器類,將Adaptee中的介面封裝成Target中的新介面,來滿足新的需求。

過程:

客戶通過目標介面調用適配器的方法對適配器發出請求,適配器使用被適配者介面把請求轉換成被適配者的一個或多個調用介面。客戶接收到調用的結果,但未察覺到一切都是適配器在起轉換作用。

源碼示例: HandlerAdapter

HandlerMapping將方法映射到URL,因此DispatcherServlet知道特定請求應該調用哪個方法。然後DispatcherServlet使用HandlerAdapter來調用該方法。

為什麼DispatcherServlet不直接調用方法?

因為有很多方式可以調用方法,比如註解、xml等等。HandlerAdapter將DispatcherServlet和被調用的操作解耦。

HandlerAdapter就是其中的適配器類,它類似於handler對象和dispatcher servlet之間的橋。

你可以從下面HandlerAdapter源程式碼中看到的,有一個返回類型是ModelAndView的handle方法。每個HandlerAdapter都會實現這個方法,將HttpServletRequest和HttpServletResponse委託給handler對象,然後handler對象將使用這些HttpServletRequest/Response執行應用程式邏輯。

Object handler) throws Exception;

此應用程式邏輯執行將生成模型和視圖。視圖可以是視圖名稱字元串或視圖對象的形式。模型包含將用於呈現視圖的數據。HandlerAdapter將在ModelAndView對象中包裝模型和視圖。處理ModelAndView對象是dispatcher servlet的工作。

Dispatcher servlet不知道Handler對象,並且不直接處理應用程式邏輯。Handler對象也不用將模型和視圖轉換為ModelAndView對象,因為HandlerAdapter會完成轉換工作。

HandlerAdapter的一些實現類:

1. SimpleServletHandlerAdapter: 適配實現 Servlet 介面的 Handler, 默認調用其 service 方法    2. SimpleControllerHandlerAdapter: 適配實現 Controller 介面的 Handler, 默認調用其 handleRequest 方法    3. HttpRequestHandlerAdapter: 適配實現 HttpRequestHandler 介面的 Handler, 默認調用其 handleRequest 方法    4. RequestMappingHandlerAdapter: 適配被@RequestMapping注釋的方式, 一般都是解析一個一個參數, 並且通過反射進行激活  

相關實現片段如下:

SimpleServletHandlerAdapter:

   @Override        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)                throws Exception {            ((Servlet) handler).service(request, response);            return null;        }  

RequestMappingHandlerAdapter>AbstractHandlerMethodAdapter:

  @Override        public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)                throws Exception {            return handleInternal(request, response, (HandlerMethod) handler);        }            @Override        protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {                ModelAndView mav;                checkRequest(request);               // Execute invokeHandlerMethod in synchronized block if required.                if (this.synchronizeOnSession) {                    HttpSession session = request.getSession(false);                    if (session != null) {                       Object mutex = WebUtils.getSessionMutex(session);                        synchronized (mutex) {                            mav = invokeHandlerMethod(request, response, handlerMethod);                        }                    }                    else {                        // No HttpSession available -> no mutex necessary                        mav = invokeHandlerMethod(request, response, handlerMethod);                    }                }                else {                    // No synchronization on session demanded at all...                    mav = invokeHandlerMethod(request, response, handlerMethod);                }                if (!response.containsHeader(HEADER_CACHE_CONTROL)) {                    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {                        applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);                    }                    else {                        prepareResponse(response);                    }                }                return mav;          }  

HttpRequestHandlerAdapter:

   @Override        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)                throws Exception {            ((HttpRequestHandler) handler).handleRequest(request, response);            return null;        }  

SimpleControllerHandlerAdapter:

@Override        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)                throws Exception {            return ((Controller) handler).handleRequest(request, response);        }  

適用場景

1、 以前開發的系統存在滿足新系統功能需求的類,但其介面同新系統的介面不一致, 需要重複使用現有的類,。

2、 想要建立一個可以重用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。

3、 使用第三方提供的組件,但組件介面定義和自己要求的介面定義不同, 不希望修改自己的介面,但是要使用第三方組件介面的功能,避免重複造輪子。

優缺點

優點:

1、將目標類和適配者類解耦

2、增加了類的透明性和復用性,將具體的實現封裝在適配者類中,對於客戶端類來說是透明的,而且提高了適配者的復用性

3、靈活性和擴展性都非常好,符合開閉原則

結構圖中展示的是對象適配器模式,即適配器實現我們的目標介面,但是並不繼承需要被適配的類,而是通過在適配器的構造函數中將需要被適配的類傳遞進來從而進行適配。

對象適配器還有的優點:

  把多個不同的適配者適配到同一個目標,也就是說,同一個適配器可以把被適配者類和他的子類都適配到目標介面。

對象適配器的缺點:

 使得override(重定義)Adaptee的行為比較困難。如果一定要override Adaptee的方法,就只好先做一個Adaptee的子類以override Adaptee的方法,然後再把這個子類當作真正的Adaptee源進行適配。

類適配器即適配器Adapter繼承被適配者Adaptee,並實現目標介面Target。

如圖:

類適配器還有的優點:

 由於Adapter是Adaptee的子類,Adapter可以override(重定義) Adaptee的方法。

類適配器的缺點:

 對於Java、C#等不支援多重繼承的語言,一次最多只能適配一個Adaptee,而且目標抽象類只能為介面,不能為類,其使用有一定的局限性。且該模式不能將一個適配者類和他的子類同時適配到目標介面。