設計模式詳解(五)----------適配器模式

来源:http://www.cnblogs.com/ZhangHaoShuaiGe/archive/2017/11/28/7911221.html
-Advertisement-
Play Games

各位朋友好,本章節我們繼續講第五個設計模式。 在生活中,我們都知道手機記憶體卡是無法直接接電腦的,因為記憶體卡的卡槽比較小,而電腦只有USB插孔,此時我們需要用到讀卡器。這個讀卡器就相當於是適配器。這是生活上的適配器,那麼在OO對象中,適配器就是將一個介面轉換成另一個介面,使得客戶可以使用。 適配器模式 ...


    各位朋友好,本章節我們繼續講第五個設計模式。 

  在生活中,我們都知道手機記憶體卡是無法直接接電腦的,因為記憶體卡的卡槽比較小,而電腦只有USB插孔,此時我們需要用到讀卡器。這個讀卡器就相當於是適配器。這是生活上的適配器,那麼在OO對象中,適配器就是將一個介面轉換成另一個介面,使得客戶可以使用。

 適配器模式從實現方式上分為兩種,類適配器和對象適配器,這兩種的區別在於實現方式上的不同,一種採用繼承,一種採用組合的方式。

下麵我們來看一個例子,下麵有兩個介面,一個是鹿(Deer),一個是狼(wolf),

public interface Deer {
    public void run();
    public void eatGrass();
}

interface Wolf{
    public void run();
    public void eatMeat();
}

 

我們讓梅花鹿(SikaDeer)和雪狼(SnowWolf)分別實現這兩個介面

class SikaDeer implements Deer{

    @Override
    public void run() {
        System.out.println("我在跑");
        
    }

    @Override
    public void eatGrass() {
        System.out.println("我在吃草");
        
    }
    
}

class SnowWolf implements Wolf{

    @Override
    public void run() {
        System.out.println("我在跑");
        
    }

    @Override
    public void eatMeat() {
        System.out.println("我在吃肉");
        
    }
    
}

假設現在狼要吃鹿,但是他要偽裝成鹿然後混進去,那麼現在因為介面不同,無法偽裝,所以現在我們幫它寫個適配器:

class  SnowAdapter implements Deer{//首先我們需要實現想轉換成的介面,也就是鹿要看到的介面
    private SnowWolf snowWolf;
    public SnowAdapter(SnowWolf snowWolf) {
        this.snowWolf = snowWolf;//接著取得我們要適配的對象的引用
    }
    @Override
    public void run() {
        snowWolf.run();//接著實現介面的方法
        
    }

    @Override
    public void eatGrass() {
        snowWolf.eatMeat();
        
    }    
}

接下來我們寫個測試類:

public class TestAdapter {
    public static void main(String[] args) {
        Deer sikaDeer = new SikaDeer();//先來個梅花鹿
        SnowWolf snowWolf = new SnowWolf();//再來個雪狼
        SnowAdapter snowAdapter = new SnowAdapter(snowWolf);//接下來是偽裝後的雪狼
        System.out.println("snowWolf:");
        snowWolf.run();
        snowWolf.eatMeat();
        
        System.out.println("sikaDeer says:");
        testDeer(sikaDeer);
        
        System.out.println("snowAdapter says:");
        testDeer(snowAdapter);
    }
    
    public static void testDeer(Deer deer){
        deer.run();
        deer.eatGrass();
    }
}

結果展示:

 

 在這裡,我們來分析一下這個過程,鹿是我們的目標介面,梅花鹿是依據鹿實現的,而適配器實現了目標介面,並持有被適配者(雪狼)的實例。

適配器模式講一個類的介面,轉換成客戶期望的另一個。適配器讓原本介面不相容的類可以合作無間。

下麵我們看一下類圖:

這種實現方式是對象適配器,接下來LZ說一下類適配器

從類圖中可以看出,客戶想要的是sampleOperation2()方法,但是Adaptee並沒有,為了使客戶能夠使用Adaptee類的sampleOperation2()方法,我們需要提供一個構造器Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關係。

這裡所涉及的角色有:

  ●  目標(Target)角色:這就是所期待得到的介面。註意:由於這裡討論的是類適配器模式,因此目標不可以是類。

  ●  源(Adapee)角色:現在需要適配的介面。

  ●  適配器(Adaper)角色:適配器類是本模式的核心。適配器把源介面轉換成目標介面。顯然,這一角色不可以是介面,而必須是具體類。

我們用代碼簡單的模仿一下類圖的關係,首先是目標角色

public interface Target {
    /**
     * 這是源類Adaptee也有的方法
     */
    public void sampleOperation1(); 
    /**
     * 這是源類Adapteee沒有的方法
     */
    public void sampleOperation2(); 
}

接著是源,

public class Adaptee {
    
    public void sampleOperation1(){}

}

接下來我們寫適配器,我們讓適配器直接繼承源,並實現目標介面,然後補充sampleOperation2()方法

public class Adapter extends Adaptee implements Target {

    @Override
    public void sampleOperation2() {
        //寫相關的代碼
    }

}

類適配器所用的是繼承,但是因為JAVA單繼承的原因,一個JAVA類只能有一個父類,所以當我們要適配的對象是兩個類的時候,我們就無可奈何了。

 接下來我們看一下類適配器與對象適配器之間的不同:

類適配器使用對象繼承的方式,是靜態的定義方式;而對象適配器使用對象組合的方式,是動態組合的方式。

對於類適配器,由於適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因為繼承是靜態的關係,當適配器繼承了Adaptee後,就不可能再去處理  Adaptee的子類了。而對於對象適配器,一個適配器可以把多種不同的源適配到同一個目標。換言之,同一個適配器可以把源類和它的子類都適配到目標介面。因為對象適配器採用的是對象組合的關係,只要對象類型正確,是不是子類都無所謂。

③  對於類適配器,適配器可以重定義Adaptee的部分行為,相當於子類覆蓋父類的部分實現方法。而對於對象適配器,要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實現重定義,然後讓適配器組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用於所有的源。

對於類適配器,僅僅引入了一個對象,並不需要額外的引用來間接得到Adaptee。而 對於對象適配器,需要額外的引用來間接得到Adaptee。

那麼到底是多用組合還是多用繼承,相信大家經過了前面四章的學習,已經非常明瞭了。LZ建議儘量使用對象適配器的實現方式,多用合成/聚合、少用繼承。不過說到底,具體問題還要進行具體分析才對,根據需要來選用實現方式,最適合的才是最好的。

適配器模式的優點

  •   更好的復用性

  系統需要使用現有的類,而此類的介面不符合系統的需要。那麼通過適配器模式就可以讓這些功能得到更好的復用。

  •   更好的擴展性

  在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴展系統的功能。

適配器模式的缺點

  過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A介面,其實內部被適配成了B介面的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構

 

上面LZ說過適配器從實現方式上分為兩種,類適配器和對象適配器,其實從使用目的上來說,也可以分為兩種,特殊適配器和預設適配器,這兩種的區別在於使用目的上的不同,一種為了復用原有的代碼並適配當前的介面,一種為了提供預設的實現,避免子類需要實現不該實現的方法。

  而我們以上兩種方式都是為了復用現有的代碼而採用的適配器模式,屬於特殊適配器,可稱為定製適配器,還有另外一種稱為預設適配器

下麵LZ舉一個簡單的例子:

interface Teacher{
    void speak();
    void listen();
    void teach();
}

這是一個老師的介面,張老師要實現它,但是幾年前,張老師還只是一個學生,並不會teach(),所以我們需要寫一個預設適配器

abstract class Adapter implements  Teacher{

    @Override
    public void speak() {}

    @Override
    public void listen() {}

    @Override
    public void teach() {}
    
}

然後讓張老師繼承它

class ZhangTeacher extends  Adapter{
    @Override
    public void speak() {
        System.out.println("speak");
    }

    @Override
    public void listen() {
        System.out.println("listen");
    }
}

  雖然這隻是一個例子,但是在很多情況下,我們必須讓一個具體類實現某一個介面,但是這個類又用不到介面所規定的所有的方法。通常的處理方法是,這個具體類要實現所有的方法,那些有用的方法要有實現,那些沒有用的方法也要有空的實現。

  這些空的方法是一種浪費,有時也是一種混亂。除非看過這些空方法的代碼,否則程式員可能會以為這些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看過這些方法的源代碼或是文檔。

  而預設適配模式可以很好的處理這一情況。可以設計一個抽象的適配器類實現介面,此抽象類要給介面所要求的每一種方法都提供一個空的方法,使它的具體子類免於被迫實現空的方法。

在任何時候,如果我們不准備實現一個介面的所有方法時,就可以使用“預設適配模式”製造一個抽象類,給出所有方法的具體實現。這樣,從這個抽象類再繼承下去的子類就不必實現所有的方法了。

這一章講完,適配器模式相信各位也有了一定的瞭解,此模式其實就是一種補救措施,在開發的時候,儘量不要用到這個模式。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • numpy.eye(N, M=None, k=0, dtype=<type ‘float’>) 生成對角矩陣 列數N 行數M 寫一個代表行數等於列數 k代表偏移量正數向上偏移,負數向下偏移 如numpy.eye(3,k=1,dtyle=int) 0 1 0 0 0 1 0 0 0 numpy.sha ...
  • 基本概念 1.操作系統中heap和stack的區別。 2.什麼是對象/關係映射集成模塊? 3.什麼是Java的反射機制? 4.什麼是ACID? 5.B/S和C/S的聯繫與區別? 6.cookie和session的區別? 7.interface與抽象類的區別. 8.IOC的優點是什麼? 9.IO和NI ...
  • 先導入模塊: from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 分頁器的使用(views函數中的代碼): templates中的代碼 ...
  • 熔斷是當某個服務調用慢或者有大量超時現象(過載),系統停止後續針對該服務的調用而直接返回,直至情況好轉才恢復調用。這通常是為防止造成整個系統故障而採取的一種保護措施,也稱過載保護。很多時候剛開始,可能只是出現了局部小規模系統故障,但後來故障影響的範圍越來越大,最終導致了全局性的後果。 ...
  • 這是曾經的一個面試題,正好引出狀態機編程思想。挺不錯的一個例子。 題目描述 給定一個字元串,它由以下字元組成: 左括弧“(” 右括弧“)” 下劃線“_” 大小寫字母構成的字元串(單字母也算作字元串) 該字元串組成有以下規則限定: 括弧成對出現且不會嵌套,保證語法正確 字元串可以出現在括弧內,也可以出 ...
  • 閑暇之餘,開發了一款休閑類app,雖然用戶量不多,但確實花了不少心血在這上面。然而,開發出來的結果,與之前想好的架構,還是有不少區別。 下麵,記錄下這款app架構的演變: 最初,只想寫個app,能與機器人進行聊天。架構隨意搭(或者說沒有架構),快速開發出來就好: 最初(無架構)版本 很簡單,按照代碼 ...
  • 對於大型網站,分層和分隔的一個主要目的是為了切分後的模塊便於分散式部署,即將不同模塊部署在不同的伺服器上,通過遠程調用協同工作。分散式意味著可以使用更多的電腦完同樣的工作,電腦越多,CPU、記憶體、存儲資源就越多,能過處理的併發訪問和數據量就越大,進而能夠為更多的用戶提供服務。 ...
  • Pipeline使用了groovy語法,同時可以使用所有jenkins插件在groovy里進行調用,可以說通過UI可以實現的功能使用pipeline也可以實現,這一點我在上一篇文章里已經說明,今天主要說一下pipeline里的公用類庫,你可以自己定義方法,一般地一個方法一個文件,擴展名為groovy ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...