每天學習一個設計模式(一):結構型之適配器模式

来源:https://www.cnblogs.com/aohongzhu/archive/2020/05/18/12938737.html

一、基本概念 適配器模式是將某個類的介面轉換成客戶端期望的另一個介面表示,目的是消除由於介面不匹配所造成的的類的相容性問題。 二、通俗解釋 ADAPTER 適配器模式:在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助於我的朋友kent了,他作為我和Sarah ...


一、基本概念

適配器模式是將某個類的介面轉換成客戶端期望的另一個介面表示,目的是消除由於介面不匹配所造成的的類的相容性問題。

二、通俗解釋

ADAPTER 適配器模式:在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助於我的朋友kent了,他作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會不會耍我) 適配器(變壓器)模式:把一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面原因不匹配而無法一起工作的兩個類能夠一起工作。適配類可以根據參數返還一個合適的實例給客戶端。

用電器做例子,筆記本電腦的插頭一般都是三相的,即除了陽極、陰極外,還有一個地極。而有些地方的電源插座卻只有兩極,沒有地極。電源插座與筆記本電腦的電源插頭不匹配使得筆記本電腦無法使用。這時候一個三相到兩相的轉換器(適配器)就能解決此問題,而這正像是本模式所做的事情。

三、分類

主要分三類:類的適配器模式、對象的適配器模式、介面的適配器模式。

1. 類的適配器模式:

class Source {
    public void method1() {
        System.out.println("This is original method...");
    }
}

interface Targetable {

    /**
     * 與原類中的方法相同
     */
    public void method1();

    /**
     * 新類的方法
     */
    public void method2();
}

class Adapter extends Source implements Targetable {

    @Override
    public void method2() {
        System.out.println("This is the targetable method...");
    }
}

public class AdapterPattern {
    public static void main(String[] args) {
        Targetable targetable = new Adapter();
        targetable.method1();
        targetable.method2();
    }
}

2. 對象的適配器模式

基本思路和類的適配器模式相同,只是將Adapter 類作修改,這次不繼承Source 類,而是持有Source 類的實例,以達到解決相容性的問題。

class Source {
    public void method1() {
        System.out.println("This is original method...");
    }
}

interface Targetable {

    /**
     * 與原類中的方法相同
     */
    public void method1();

    /**
     * 新類的方法
     */
    public void method2();
}

class Wrapper implements Targetable {

    private Source source;

    public Wrapper(Source source) {
        super();
        this.source = source;
    }

    @Override
    public void method1() {
        source.method1();
    }

    @Override
    public void method2() {
        System.out.println("This is the targetable method...");
    }
}

public class AdapterPattern {
    public static void main(String[] args) {
        Source source = new Source();
        Targetable targetable = new Wrapper(source);
        targetable.method1();
        targetable.method2();
    }
}

3. 介面的適配器模式


介面的適配器是這樣的:有時我們寫的一個介面中有多個抽象方法,當我們寫該介面的實現類時,必須實現該介面的所有方法,這明顯有時比較浪費,因為並不是所有的方法都是我們需要的,有時只需要某一些,此處為瞭解決這個問題,我們引入了介面的適配器模式,藉助於一個抽象類,該抽象類實現了該介面,實現了所有的方法,而我們不和原始的介面打交道,只和該抽象類取得聯繫,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行。

/**
 * 定義埠介面,提供通信服務
 */
interface Port {
    /**
     * 遠程SSH埠為22
     */
    void SSH();

    /**
     * 網路埠為80
     */
    void NET();

    /**
     * Tomcat容器埠為8080
     */
    void Tomcat();

    /**
     * MySQL資料庫埠為3306
     */
    void MySQL();
}

/**
 * 定義抽象類實現埠介面,但是什麼事情都不做
 */
abstract class Wrapper implements Port {
    @Override
    public void SSH() {

    }

    @Override
    public void NET() {

    }

    @Override
    public void Tomcat() {

    }

    @Override
    public void MySQL() {

    }
}

/**
 * 提供聊天服務
 * 需要網路功能
 */
class Chat extends Wrapper {
    @Override
    public void NET() {
        System.out.println("Hello World...");
    }
}

/**
 * 網站伺服器
 * 需要Tomcat容器,Mysql資料庫,網路服務,遠程服務
 */
class Server extends Wrapper {
    @Override
    public void SSH() {
        System.out.println("Connect success...");
    }

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

    @Override
    public void Tomcat() {
        System.out.println("Tomcat is running...");
    }

    @Override
    public void MySQL() {
        System.out.println("MySQL is running...");
    }
}

public class AdapterPattern {

    private static Port chatPort = new Chat();
    private static Port serverPort = new Server();

    public static void main(String[] args) {
        // 聊天服務
        chatPort.NET();

        // 伺服器
        serverPort.SSH();
        serverPort.NET();
        serverPort.Tomcat();
        serverPort.MySQL();
    }
}

介面的適配器模式又稱為預設適配器模式

  預設適配(Default Adapter)模式為一個介面提供預設實現,這樣子類型可以從這個預設實現進行擴展,而不必從原有介面進行擴展。作為適配器模式的一個特例,預設是適配模式在JAVA語言中有著特殊的應用。

魯智深的故事

  和尚要做什麼呢?吃齋、念經、打坐、撞鐘、習武等。如果設計一個和尚介面,給出所有的和尚都需要實現的方法,那麼這個介面應當如下:

public interface 和尚 {
    public void 吃齋();
    public void 念經();
    public void 打坐();
    public void 撞鐘();
    public void 習武();
    public String getName();
}

顯然,所有的和尚類都應當實現介面所定義的全部方法,不然就根本通不過JAVA語言編輯器。像下麵的魯智深類就不行。

public class 魯智深 implements 和尚{
    public void 習武(){
        拳打鎮關西;
        大鬧五台山;
        大鬧桃花村;
        火燒瓦官寺;
        倒拔垂楊柳;
    }
    public String getName(){
        return "魯智深";
    }
}

由於魯智深只實現了getName()和習武()方法,而沒有實現任何其他的方法。因此,它根本就通不過Java語言編譯器。魯智深類只有實現和尚介面的所有的方法才可以通過Java語言編譯器,但是這樣一來魯智深就不再是魯智深了。以史為鑒,可以知天下。研究一下幾百年前魯智深是怎麼剃度成和尚的,會對Java編程有很大的啟發。不錯,當初魯達剃度,眾僧說:“此人形容醜惡、相貌凶頑,不可剃度他",但是長老卻說:”此人上應天星、心地剛直。雖然時下凶頑,命中駁雜,久後卻得清凈。證果非凡,汝等皆不及他。”

  原來如此!看來只要這裡也應上一個天星的話,問題就解決了!使用面向對象的語言來說,“應”者,實現也;“天星”者,抽象類也。

public abstract class 天星 implements 和尚 {
    public void 吃齋(){}
    public void 念經(){}
    public void 打坐(){}
    public void 撞鐘(){}
    public void 習武(){}
    public String getName(){
        return null;
    }
}

魯智深類繼承抽象類“天星”

public class 魯智深 extends 和尚{
    public void 習武(){
        拳打鎮關西;
        大鬧五台山;
        大鬧桃花村;
        火燒瓦官寺;
        倒拔垂楊柳;
    }
    public String getName(){
        return "魯智深";
    }
}

這個抽象的天星類便是一個適配器類,魯智深實際上藉助於適配器模式達到了剃度的目的。此適配器類實現了和尚介面所要求的所有方法。但是與通常的適配器模式不同的是,此適配器類給出的所有的方法的實現都是“平庸”的。這種“平庸化”的適配器模式稱作預設適配模式。

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

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

  預設適配模式可以很好的處理這一情況。可以設計一個抽象的適配器類實現介面,此抽象類要給介面所要求的每一種方法都提供一個空的方法。就像幫助了魯智深的“上應天星”一樣,此抽象類可以使它的具體子類免於被迫實現空的方法。

四、類適配器和對象適配器的權衡

  • 類適配器使用對象繼承的方式,是靜態的定義方式;而對象適配器使用對象組合的方式,是動態組合的方式。
  • 對於類適配器由於適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因為繼承是靜態的關係,當適配器繼承了Adaptee後,就不可能再去處理  Adaptee的子類了。

  • 對於對象適配器一個適配器可以把多種不同的源適配到同一個目標。換言之,同一個適配器可以把源類和它的子類都適配到目標介面。因為對象適配器採用的是對象組合的關係,只要對象類型正確,是不是子類都無所謂。

  • 對於類適配器適配器可以重定義Adaptee的部分行為,相當於子類覆蓋父類的部分實現方法。

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

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

  • 對於對象適配器,需要額外的引用來間接得到Adaptee。

建議儘量使用對象適配器的實現方式,多用合成或聚合、少用繼承。當然,具體問題具體分析,根據需要來選用實現方式,最適合的才是最好的。

適配器模式的優點

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

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

適配器模式的缺點

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

適配器模式的用意是要改變源的介面,以便於目標介面相容。預設適配的用意稍有不同,它是為了方便建立一個不平庸的適配器類而提供的一種平庸實現。

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


(引用:《JAVA與模式》之適配器模式Java 中幾種常用設計模式java常用的設計模式


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

更多相關文章
  • 前提: (1) 相關博文地址: SpringBoot + Vue + ElementUI 實現後臺管理系統模板 -- 前端篇(一):搭建基本環境:https://www.cnblogs.com/l-y-h/p/12930895.html SpringBoot + Vue + ElementUI 實現 ...
  • 迭代器模式是一種使用頻率非常高的設計模式,迭代器用於對一個聚合對象進行遍歷。通過引入迭代器可以將數據的遍歷功能從聚合對象中分離出來,聚合對象只負責存儲數據,聚合對象只負責存儲數據,而遍曆數據由迭代器來完成。 模式動機 一個聚合對象,如一個列表(List)或者一個集合(Set),應該提供一種方法來讓別 ...
  • 代理的本質無論任何時候,只要談到設計模式,大腦中一定要蹦出這四個字“活學活用”。要想對某個事物做到活學活用,必須要對它足夠瞭解,甚至要剖析到本質才行。總是會有些人說,我幹嘛要知道原理,幹嘛要去看源碼?會用就行了。對於這種情況,我只有五個字相送,“你開心就好”。不可否認,認識一個陌生事物,大部分情況還 ...
  • 一、JML初探 ​ 作為一種形式化語言,可以約束 代碼中類和方法的狀態和行為形成規格,通過將一系列具體代碼實現抽象成明確的行為介面,可以形成一種契約式編程模式, 設計者無需考慮實際的數據結構與演算法,可以聚焦於程式的整體邏輯, 形式化語言的無二義性能讓實現者準確理解介面功能,根據問題需要選擇合適的實現 ...
  • 一、基本概念 門面模式(外觀模式)是對象的結構模式,外部與一個子系統的通信必須通過一個統一的門面對象進行。門面模式提供一個高層次的介面,使得子系統更易於使用。 二、通俗解釋 FACADE門面模式:我有一個專業的Nikon相機,我就喜歡自己手動調光圈、快門,這樣照出來的照片才專業,但MM可不懂這些,教 ...
  • 一、基本概念 裝飾模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。 二、通俗解釋 DECORATOR裝飾模式:Mary過完輪到Sarly過生日,還是不要叫她自己挑了,不然這個月伙食費肯定玩完,拿出我去年在華山頂上照的照片,在背面寫上“最好的的 ...
  • 一、基本概念 合成模式屬於對象的結構模式,有時又叫做“部分——整體”模式。合成模式將對象組織到樹結構中,可以用來描述整體與部分的關係。合成模式可以使客戶端將單純元素與複合元素同等看待。 二、通俗解釋 COMPOSITE合成模式:Mary今天過生日。“我過生日,你要送我一件禮物。”“嗯,好吧,去商店, ...
  • 一、基本概念 橋梁模式(Bridge)是對象的結構模式。又稱為柄體(Handle and Body)模式或介面(Interface)模式。橋梁模式的用意是“將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化”。 這句話有三個關鍵詞,也就是抽象化、實 ...
一周排行
  • 比如要拆分“呵呵呵90909086676喝喝999”,下麵當type=0返回的是中文字元串“呵呵呵,喝喝”,type=1返回的是數字字元串“90909086676,999”, private string GetStrings(string str,int type=0) { IList<strin ...
  • Swagger一個優秀的Api介面文檔生成工具。Swagger可以可以動態生成Api介面文檔,有效的降低前後端人員關於Api介面的溝通成本,促進項目高效開發。 1、使用NuGet安裝最新的包:Swashbuckle.AspNetCore。 2、編輯項目文件(NetCoreTemplate.Web.c ...
  • 2020 年 7 月 30 日, 由.NET基金會和微軟 將舉辦一個線上和為期一天的活動,包括 微軟 .NET 團隊的演講者以及社區的演講者。本次線上大會 專註.NET框架構建微服務,演講者分享構建和部署雲原生應用程式的最佳實踐、模式、提示和技巧。有關更多信息和隨時瞭解情況:https://focu... ...
  • #abp框架Excel導出——基於vue #1.技術棧 ##1.1 前端採用vue,官方提供 UI套件用的是iview ##1.2 後臺是abp——aspnetboilerplate 即abp v1,https://github.com/aspnetboilerplate/aspnetboilerp ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:碧茂大數據 PS:如有需要Python學習資料的小伙伴可以加下方的群去找免費管理員領取 input()輸入 Python提供了 input() 內置函數從標準輸入讀入一 ...
  • 從12年到20年,python以肉眼可見的趨勢超過了java,成為了當今It界人人皆知的編程語言。 python為什麼這麼火? 網路編程語言搜索指數 適合初學者 Python具有語法簡單、語句清晰的特點,這就讓初學者在學習階段可以把精力集中在編程對象和思維方法上。 大佬都在用 Google,YouT ...
  • 在社會上存在一種普遍的對培訓機構的學生一種歧視的現象,具體表現在,比如:當你去公司面試的時候,一旦你說了你是培訓機構出來的,那麼基本上你就涼了,那麼你瞞著不說,然後又通過了面試成功入職,但是以後一旦在公司被髮現有培訓經歷,可能會面臨被降薪,甚至被辭退,培訓機構出來的學生,在用人單位眼裡就是能力低下的 ...
  • from typing import List# 這道題看了大佬寫的代碼,經過自己的理解寫出來了。# 從最外圍的四周找有沒有為O的,如果有的話就進入深搜函數,然後深搜遍歷# 判斷上下左右的位置是否為Oclass Solution: def solve(self, board: List[List[s ...
  • import requests; import re; import os; # 1.請求網頁 header = { "user-agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, li ...
  • import requests; import re; import os; import parsel; 1.請求網頁 header = { "user-agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537. ...