主頁 > 軟體設計 > 編碼中的Adapter,不僅是一種設計模式,更是一種架構理念與解決方案

編碼中的Adapter,不僅是一種設計模式,更是一種架構理念與解決方案

2022-09-27 09:03:55 軟體設計

大家好,又見面了,

不知道下面這玩意大家有沒有見過或者使用過?這是一個插座轉換器,我們都知道日常使用的是220v的交流電,而國外不同國家使用的電流電壓是不一樣的(比如日本使用的是110v)、且插座的介面樣式也是各不相同的(比如歐洲國家使用的是兩個小圓柱狀的插頭介面),如果我們到別的國家去旅行的時候,借助這個插座轉換器,就可以讓我們的手機充電器在國外也能正常使用了,

當然,除了使用插座轉換器,還有個方法也可以讓我們出國之后正常的使用各種電子產品,那就是在當地重新買一套!顯然,這樣的成本就會非常巨大,明顯不符合我們 勤(nang)儉(zhong)持(xiu)家(se) 的特征,

看過我前面的文章的小伙伴應該知道,我的文章中一直反復的在闡述自己的一個觀念,即“編碼源于生活” ,這里依舊不例外,現實生活中的樸素哲學思維,在代碼世界中其實也無時無刻不在體現著,上面舉的例子,在我們的專案中又何嘗不是在頻繁上演此類情況呢?

我們先按照原有的業務邏輯實作了一套代碼,后來又來了個新的需求,如果重新開發一套需要投入大量的人力物力,所以首選方案就是去思考如何去復用已有的邏輯,以最小的代價將業務對接適配使用現有的邏輯去實作,

本篇文章中,我們就從這個“插座轉換器”來作為切入點,聊一聊在軟體系統中無處不在的“插座轉換器” —— 編碼中的配接器(Adapter),選定以Adapter為題材進行闡述,并非是因為Adapter在技術實作上有多復雜,其實Adapter真正實作起來是非常簡單的,而且很多人有意或無意中其實也都在使用,更多的是想一起探討這種借助Adapter來復用與兼容已有邏輯的思路,以及如何利用Adapter來踐行OCP(開閉原則)的系統架構設計理念

Adapter的百媚千姿

新瓶舊酒:復用現成的實作邏輯

新瓶裝舊酒,在我們的系統里面是一個很“節省”的操作,可以讓我們基于一個現有的能力快速的封裝提供出一個全新的業務功能,當然有的時候,系統現有的能力可能會某些方面無法完全滿足新業務的需求,需要做一些轉換適配處理,

舉個例子:

一個視頻網站,原先已有一個評論能力,用戶可以在視頻下方發表評論,然后評論內容以串列的形式展示在視頻下方頁面上,現在需要開發一個新功能,支持視頻發送彈幕能力,并將彈幕顯示在視頻播放畫面上,

從需求功能上來說,評論彈幕有很多相似之處,對后端而言,其處理邏輯與存盤資料結構幾乎都是相同的,只是在資料串列API實作的時候,需要過濾出評論資訊展示到評論區、或者過濾出彈幕資訊顯示到視頻畫面上,但是由于彈幕資訊有一些特殊的屬性,又沒法直接完全使用現有的評論介面,比如彈幕可能會設定顯示在螢屏的位置、彈幕的字體顏色等等,這種情況下,我們可以通過構造個Adapter配接器的方式,在復用已有評論能力的基礎上,順便擴展實作需要的彈幕新特性,

如上圖所示,我們可以在Adapter中封裝擴展彈幕需要的新特性,然后對于資料存盤等邏輯則直接復用已有的評論功能處理邏輯,這樣就可以大大減少我們的開發作業量、后續也只需要維護一套主體代碼即可,

負重前行:兼容歷史版本

和上面討論的場景相反,實際開發中還有一種非常常見的情況,就是原先的時候實作了一套業務邏輯,然后因為業務變化或者系統重構,需要對底層具體實作邏輯進行大改,這種情況下,為了保證此前呼叫該API的業務可以正常使用,通常有兩種思路:

  1. 保持原先的內容不動,完全另起爐灶全新實作一套,然后兩套邏輯并存,同時維護;

  2. 按照新的邏輯去實作,并將原先的對外API適配轉換對接使用新邏輯實作,

顯然,從成本與可維護性層面考慮,思路2更為可行、更加經濟

對比我們文首舉的那個“插頭轉換器”的例子,我們可以把圖中V1版本業務邏輯當做我們國內的手機充電插頭,而圖中綠色部分的V2新版本依賴邏輯,則是歐洲地區的圓孔墻面插座,那么如何讓國標的扁口插頭能用上歐標的圓孔插座呢?關鍵就是那個插頭轉換器(Adapter),

另類心機:屏蔽開源協議傳染

大家可以回想下,曾經是否也有過從github上“借鑒”一些代碼放進了自己的專案中,然后簡單修改為符合自己訴求的邏輯,便當做是自研代碼去正常使用了?不知道你是否有關注過你所拷貝的代碼所對應的開源協議呢?要小心啦、這個看似平常的操作,也許會給專案埋下致命隱患

為什么說的這么危言聳聽呢?因為有一些不太友好的開源協議(比如GPL協議),會要求使用了其代碼的專案如果商用就必須要開源其全部原始碼!而對于很多軟體公司而言,原始碼便是公司的核心資產,是公司最為核心的競爭力,將原始碼開源無異于是要了老板和公司的命,也許有人會對此很不屑,大家都這么干,似乎并沒有發現有人來追責呢?有個詞叫做“樹大招風”,只要你的產品做的夠大,就一定會被盯上 —— 你品,你細品,在當前知識著作權保護越來越強的情況下,我們還是應該關注并提前做好應對這種危機出現的可能,避免埋下隱患,

這種情況下,可以基于Adapter的機制,實作棄卒保車的效果,即構建一個適配層,然后僅將適配層進行開源,而核心的模塊代碼中,則通過介面呼叫的方式使用適配層即可,這樣避免了核心模塊代碼被開源協議傳染,由于核心模塊中并沒有集成被二次改動后的開源原始碼,所以也不具有開放原始碼的義務、而Adapter層沒有任何核心業務邏輯,即使開源對公司、對專案也沒有影響,

基于Adapter適配層的方式來切斷開源協議傳染的成功實踐,最典型的莫過于Android專案(AOSP)了,因為AOSP是基于Linux kernel內核進行構建的,而Linux Kernel使用的是GPL協議,那么按照要求,AOSP也需要開源其原始碼,但是問題來了,如果AOSP開源原始碼了,勢必導致所有基于Android定制的各個硬體廠商底層的設備驅動相關的代碼也都要全部開源,顯然不會有公司愿意這么干,

為了讓各個公司可以放心的基于Android去開發自己的產品,AOSP將自己的協議搞成了Apache開源協議,這樣對產商而言就非常友好了,無需將自己的核心原始碼開源,那么Google是如何做到將本來需要以GPL協議開源的AOSP給變為使用Apache協議開源的呢?其實就是做了一個Adapter —— 也即HALHardware Abstract Layer,硬體抽象層),

Adapter是一種理念

關于編碼中的Adapter,常規的檔案或者資料中,往往都是指的狹義上的配接器,也就是代碼class類維度的Adapter

我們跳出純粹的編碼層面,站到全域系統架構視角去審視的時候,其實Adapter在系統架構與編碼設計中是一個比較寬泛的概念,我個人更愿意Adapter看做是一種問題解決的思想、一種方案設計的理念

根據要解決的問題level范圍的不同,Adapter對應的粒度與呈現形態也會有差異,

服務型Adapter

如果是在一個分布式微服務系統中,訊息推送能力可以預見的會提供給很多不同的服務節點去呼叫,則可以將訊息推送能力也封裝為一個對外微服務,業務通過RPC或者HTTP等方式進行遠程呼叫,

這種是一種相對High Level的Adapter抽象使用(但抽象為服務獨立部署后,其實也不僅僅是個Adapter了),廣泛的應用于系統架構層面,是解決系統功能復用、業務解耦的一種有效手段,

在我此前的一篇文章中,介紹了一個構建通用在線檔案預覽服務的實際案例,里面對“預覽編輯服務”的定位就是一個典型的服務型Adapter,如下圖所示,通過預覽編輯服務這個Adapter,將檔案預覽能力所涉及的后端對接OnlyOffice或者對接kkFileView等細節邏輯給屏蔽掉,業務服務通過Adapter進行呼叫,大大簡化了業務的使用復雜度,也保持了業務模塊與檔案預覽服務內部模塊之間的耦合,

服務型Adapter著眼解決的是系統行程層面的適配與統一封裝,自身既是一個Adapter,又是一個獨立的服務,封裝內部細節差異化的實作,保證其它行程服務相對簡單的呼叫邏輯,

依賴庫型Adapter

在一些中小型專案中,會有若干個業務模塊中會用到訊息發送的能力,但是整體體量與業務規劃層面而言,卻也無需單獨部署一個專門的訊息推送服務行程,這種情況下,可以將其封裝為一個依賴庫,比如JAVA中的一個jar包,或者C++中的一個so庫檔案,亦或是C#中的dll庫檔案,這樣各個業務模塊可以集成此庫檔案,直接進行API呼叫即可,

此種型別的Adapter實作,在很多的框架中非常常見,比如在JAVA中的SpringBoot中的日志框架,底層可以選擇是使用logback,也可以選擇切換到log4j

代碼類Adapter

在單個專案模塊中,我們為了保持業務邏輯的清晰與獨立,也會通過Adapter類的方式,來解耦具體的業務邏輯,比如這里的訊息推送服務,如果僅當前模塊需要使用,則可以創建一個獨立的Adapter類,提供介面供其他類呼叫,在Adapter類中完成具體邏輯的封裝實作,

還是以前面舉的告警通知訊息發送的例子來說明,使用Adapter方式隔離訊息通道與業務邏輯的實作UML圖如下:

代碼類的Adapter在實際專案中使用的場景非常的廣泛,是用于屏蔽代碼底層差異化邏輯的不二選擇,在總結各種實際使用場景與優秀實踐的基礎上,演進為23種設計模式之一的配接器模式

下面我們一起聊一聊配接器模式,

Adapter是一種設計模式

所謂設計模式,便是將常規代碼編碼中常遇到的一些場景的處理方式進行了總結與抽象,固化成一個優秀實踐范例模板,使其整體實作更符合設計原則的要求,也就是說:設計模式并非是憑空捏造的,其實就是來源于常規的編碼實踐總結

按照通俗意義上對代碼設計模式的理解,配接器模式也可以分為2種形式,即類配接器模式物件配接器模式

下面分別闡述下,

類配接器模式

類配接器模式整體非常的簡單,涉及的角色也很少,類配接器模式中,Adapter與被適配的Adaptee之間,通過繼承的方式來實作,其UML圖如下所示,

主要角色說明如下:

  • Adaptee:原始被適配的類,即不符合訴求需要由Adapter進行適配的原始介面

  • Adapter:配接器本身,也是類配接器模式的核心,用于將Adaptee適配為目標的Target,

  • Target:期待獲取到的目標結果,也即Adaptee經由Adapter適配后得到的統一的目標介面

還是以前面的告警通知發送的場景為例,我們按照聚合的方式,演示下對應的Adapter實作邏輯,

@Service
public class MsgSendAdapter extends SmsSender implements IMsgSender {
    @Override
    public void send(AlarmDetail detail) {
        // detail轉SMS請求體的邏輯
        SmsContent sms = convertToSmsContent(detail);
        super.sendSms(sms);
    }
}

上述代碼中,MsgSendAdapter繼承了SmsSender類并且實作了IMsgSender介面,將父類SmsSender中的sendSms介面轉換為了IMsgSender介面提供的目標介面send(),業務可以呼叫IMsgSender.send()介面,實作對SmsSender.sendSms()邏輯的呼叫,

物件配接器模式

物件配接器模式與類配接器模式類似,區別點在于Adapter與被適配的Adaptee之間非繼承關系,而是物件組合關系,其UML圖如下:

按照物件配接器的設計思路,其代碼可以如下方式來實作:

@Service
public class MsgSendAdapter implements IMsgSender {
    @Autowired
    private SmsSender smsSender;

    @Override
    public void send(AlarmDetail detail) {
        // detail轉SMS請求體的邏輯
        SmsContent sms = convertToSmsContent(detail);
        smsSender.sendSms(sms);
    }
}

上述代碼中,MsgSendAdapter類中以組合的方式持有SmsSender物件(Adaptee),相比較類配接器的繼承邏輯,靈活性更高,所以物件配接器要更加的靈活與實用(其實在架構設計領域也一直有一種觀點叫“組合優于繼承”,因為繼承打破了面向物件的封裝),

總結回顧

好啦,關于Adapter相關的討論與個人的理解,這里就給大家分享到這里,Adapter不僅是一個簡單的具體實作類,也不僅僅是23種設計模式之一,更是一種問題解決的思想、一種方案設計的理念,

關于本篇檔案中的內容,不知道螢屏前的各位小伙伴是否在專案中有使用過Adapter或者Adapter模式來幫助自己實作某些功能呢?是否對Adapter還有一些別的獨到見解呢?歡迎評論區留言一起交流下,

我是悟道,聊技術、又不僅僅聊技術~

如果覺得有用,請點贊 + 關注讓我感受到您的支持,也可以關注下我的公眾號【架構悟道】,獲取更及時的更新,

期待與你一起探討,一起成長為更好的自己,

本文來自博客園,作者:架構悟道,歡迎關注公眾號[架構悟道]持續獲取更多干貨,轉載請注明原文鏈接:https://www.cnblogs.com/softwarearch/p/16732534.html

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/509649.html

標籤:其他

上一篇:原型模式(創建型)

下一篇:初識設計模式 - 橋接模式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more