6.java設計模式之介面卡模式

xiaokantianse發表於2020-11-17

基本需求:

  • 將一個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模式最大的作用還是將原本不相容的介面融合在一起工作
  • 實際開發中,實現起來不拘泥於我們講解的三種經典形式

相關文章