6.java設計模式之適配器模式

基本需求:

  • 將一個220V的電壓輸出成5V的電壓,其中220V電壓為被適配者,變壓器為適配器,5v電壓為適配目標

基本介紹:

  • 適配器模式屬於結構型模式,將某個類的介面轉換成客戶端期望的另一個介面表示,主的目的是兼容性,讓原本因介面不匹配不能一起工作的兩個類可以協同工作。其別名為包裝器(Wrapper) 分為類適配器模式,對象適配器模式,介面適配器模式
  • 用戶的角度看不到被適配者,是解耦的,用戶調用適配器轉化出來的目標介面方法,適配器再調用被適配者的相關介面方法

類適配器模式:

  • Adapter 類,通過繼承 src 類,實現 dst 類介面,完成 src->dst 的適配

  • UML類圖

  • 程式碼實現

    • public class Voltage220V {
      
      // 被適配類
      
      public int output220V() {
        System.out.println("輸出220V電壓");
        return 220;
      }
      
      }
      
      public interface Voltage5V {
      
      // 用戶需要使用的介面
      int output5V();
      
      }
      
      public class VoltageAdapter extends Voltage220V implements Voltage5V {
      
      // 適配方法(通過實現用戶使用的介面將配適配的類轉換成用戶所需要的)
      @Override
      public int output5V() {
        int output = output220V() / 44;
        System.out.println("適配出5V電壓");
        return output;
      }
      
      }
      
      public class Phone {
      
      // 使用介面
      public void charging(Voltage5V voltage5V) {
        voltage5V.output5V();
      }
      
      }
      
      // client調用
      public static void main(String[] args) {
      Phone phone = new Phone();
      // 用戶只關心介面 不需要關心被適配者
      phone.charging(new VoltageAdapter());
      }
      
  • 注意事項

    • Java是單繼承機制,所以類適配器需要繼承src類這一點算是一個缺點, 因為這要求dst必須是介面,有一定局限性
    • src類的方法在Adapter中都會暴露出來,也增加了使用的成本
    • 由於其繼承了src類,所以它可以根據需求重寫src類的方法,使得Adapter的靈活性增強了

對象適配器模式:

  • 基本思路和類的適配器模式相同,只是將Adapter類作修改,不是繼承src類,而是持有src類的實例,以解決兼容性的問題。即:持有src類,實現dst類介面,完成 src->dst的適配,將被適配者類的對象聚合組合到適配器類中

  • 根據「 合成復用原則」,在系統中盡量使用 關聯關係(聚合)來替代繼承關係

  • UML類圖

  • 程式碼實現

    • // 只需要對類適配器模式中的適配器類進行修改即可
      public class VoltageAdapter implements Voltage5V {
      
      // 直接將被適配類對象聚合到適配器中,免去了繼承
      // 根據「 合成復用原則」,在系統中盡量使用 關聯關係(聚合)來替代繼承關係
      private Voltage220V voltage220V;
      
      public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
      }
      // 適配方法
      @Override
      public int output5V() {
        int output = voltage220V.output220V() / 44;
        System.out.println("適配出5V電壓");
        return output;
      }
      
      }
      
  • 注意事項

    • 對象適配器和類適配器其實算是同一種思想,只不過實現方式不同。根據合成復用原則,使用組合替代繼承,所以它解決了類適配器必須繼承src的局限性問題,也不再要求 dst必須是介面
    • 使用成本更低,更靈活

介面適配器模式:

  • 核心思路:當不需要全部實現介面提供的方法時,可先設計一個抽象類實現介面,並為該介面中每個方法提供一個默認實現(空方法),那麼該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求

  • 適用於一個介面不想使用其所有的方法的情況

  • UML類圖

  • 程式碼實現

    • public class Voltage220V {
      
         // 被適配類
      
         public int output220V() {
             System.out.println("輸出220V電壓");
             return 220;
         }
      
      }
      
      public interface OutputVoltage {
      
         // 該介面提供多種方法,適配器抽象類對該介面的全部方法進行空實現
         // 使用時用戶只重寫他們關心的那個方法即可,不需要關心其他的方法
         int output5V();
      
         int output10V();
      
         int output220V();
      
      }
      
      public abstract class VoltageAdapter implements OutputVoltage {
      
         // 聚合被適配類
         protected Voltage220V voltage220V;
      
         public VoltageAdapter(Voltage220V voltage220V) {
             this.voltage220V = voltage220V;
         }
      
         // 對介面中的所有適配方法進行空實現
         @Override
         public int output5V() {
             return 0;
         }
      
         @Override
         public int output10V() {
             return 0;
         }
      
         @Override
         public int output220V() {
             return 0;
         }
      
      }
      
      public class Client {
         public static void main(String[] args) {
             VoltageAdapter voltageAdapter = new VoltageAdapter(new Voltage220V()) {
                 // 使用時用戶只重寫他們關心的那個方法即可,不需要關心其他的方法
                 @Override
                 public int output5V() {
                     int i = super.voltage220V.output220V() / 44;
                     System.out.println("適配出5V電壓");
                     return i;
                 }
             };
             voltageAdapter.output5V();
         }
      }
      

springmvc源碼:

  • springMVC中DispatchServlet中的doDispatch方法 就用到了適配器模式,通過Handler對象適配出了HandlerAdapter對象,通過HandlerAdapter執行Handler對象中的方法

  • DispatcherServlet -> HandlerMapping(得到處理器鏈) -> HandlerAdapter(處理器適配器) -> Handler(處理器) -> ViewAndResolver(視圖解析器) -> 模板等返回給瀏覽器

  • 簡單實現DispatcherServlet中的適配器

    • UML類圖

    • 程式碼實現

      • // 處理器適配器介面及實現類
        public interface MyHandlerAdapter {
        
           // 處理器適配器 判斷是那種適配器
           boolean support(Object object);
        
           // 通過適配器執行處理器中的方法
           void handler(Object object);
        
        }
        
        class MyHttpRequestHandlerAdapter  implements MyHandlerAdapter{
        
           @Override
           public boolean support(Object object) {
               return object instanceof MyHttpRequestHandler;
           }
        
           @Override
           public void handler(Object object) {
               MyHttpRequestHandler myHttpRequestHandler = (MyHttpRequestHandler) object;
               myHttpRequestHandler.doHttpRequest();
           }
        }
        
        class MySimpleControllerHandlerAdapter  implements MyHandlerAdapter{
        
           @Override
           public boolean support(Object object) {
               return object instanceof MySimpleControllerHandler;
           }
        
           @Override
           public void handler(Object object) {
               MySimpleControllerHandler mySimpleControllerHandler = (MySimpleControllerHandler) object;
               mySimpleControllerHandler.doSimpleController();
           }
        }
        
        class MySimpleServletHandlerAdapter  implements MyHandlerAdapter{
        
           @Override
           public boolean support(Object object) {
               return object instanceof MySimpleServletHandlerAdapter;
           }
        
           @Override
           public void handler(Object object) {
               MySimpleServletHandler mySimpleServletHandler = (MySimpleServletHandler) object;
               mySimpleServletHandler.doSimpleServlet();
           }
        }
        
      • // 處理器介面及實現類
        public interface MyHandler {
        
           // 處理器介面
        }
        
        class MyHttpRequestHandler implements MyHandler {
        
           public void doHttpRequest() {
               System.out.println("doHttpRequest...");
           }
        
        }
        
        class MySimpleControllerHandler implements MyHandler {
        
           public void doSimpleController() {
               System.out.println("doSimpleController...");
           }
        
        }
        
        class MySimpleServletHandler implements MyHandler {
        
           public void doSimpleServlet() {
               System.out.println("doSimpleServlet...");
           }
        
        }
        
        
      • public class MyDispatcherServlet {
        
           // 進行spring mvc中DispatcherServlet的簡單實現
           private static List<MyHandlerAdapter> myHandlerAdapters = new ArrayList<>();
        
           public MyDispatcherServlet() {
               myHandlerAdapters.add(new MyHttpRequestHandlerAdapter());
               myHandlerAdapters.add(new MySimpleControllerHandlerAdapter());
               myHandlerAdapters.add(new MySimpleServletHandlerAdapter());
           }
        
           public void doDispatcher(String request) {
               // 實際執行流程 DispatcherServlet -> HandlerMapping(得到處理器鏈) -> HandlerAdapter(處理器適配器) -> Handler(處理器) -> ViewAndResolver(視圖解析器) -> 模板等返回
               // 實際 通過HttpServletRequest對象獲取的Handler對象 此處簡化即可
               MyHttpRequestHandler myHttpRequestHandler = new MyHttpRequestHandler();
               MyHandlerAdapter handlerAdapter = getHandlerAdapter(myHttpRequestHandler);
               // 通過獲取到的HandlerAdapter對象來執行指定種類Handler的方法
               // 不同的HandlerAdapter執行Handler的方法的方式不一樣
               // 感覺相當於將Handle對象分成了多類,每類通過自己的HandlerAdapter執行Handler對象中的處理方法,這樣每類的執行方式都一樣
               handlerAdapter.handler(myHttpRequestHandler);
           }
        
           /**
            * 通過Handler獲取對應的HandlerAdapter進行適配
            * @param myHandler
            * @return
            */
           public MyHandlerAdapter getHandlerAdapter(MyHandler myHandler) {
               if (null != this.myHandlerAdapters) {
                   for (MyHandlerAdapter myHandlerAdapter : myHandlerAdapters) {
                       if (myHandlerAdapter.support(myHandler)) {
                           return myHandlerAdapter;
                       }
                   }
               }
               throw new RuntimeException("該Handler沒有對應的HandlerAdapter");
           }
        
           public static void main(String[] args) {
               MyDispatcherServlet myDispatcherServlet = new MyDispatcherServlet();
               myDispatcherServlet.doDispatcher("url");
           }
        }
        
        

注意事項:

  • 三種命名方式,是根據src是以怎樣的形式給到Adapter(在 Adapter 里的形式)來命名的
    • 類適配器:以類給到,在Adapter里,就是將src當做類,繼承
    • 對象適配器:以對象給到,在Adapter里,將src作為一個對象,持有
    • 介面適配器:以介面給到,在Adapter里,將src作為一個介面,實現
  • Adapter模式最大的作用還是將原本不兼容的介面融合在一起工作
  • 實際開發中,實現起來不拘泥於我們講解的三種經典形式