閑話設(shè)計(jì)模式之適配器模式

模式定義

適配器模式(Adapter Pattern)保持現(xiàn)有類功能不變,只是負(fù)責(zé)將一類接口轉(zhuǎn)換為另一類開發(fā)者希望的接口。它還有另一個(gè)別名叫包裝器(Wapper)。

需求場(chǎng)景

用現(xiàn)實(shí)案例來(lái)參照。適配器模式在現(xiàn)實(shí)生活中最典型的參照對(duì)象就是電源適配器。如美國(guó)和日本標(biāo)準(zhǔn)電壓是110V,而中國(guó)的是220V,通過(guò)電源適配器就可以將當(dāng)?shù)仉娫崔D(zhuǎn)換成自己設(shè)備需要的電壓。

這個(gè)案例非常符合適配器所要解決問(wèn)題的特征:

  1. 我們需要使用現(xiàn)有的模塊解決問(wèn)題。(當(dāng)?shù)仉娫矗?/li>
  2. 現(xiàn)有模塊提供的接口不符合我們開發(fā)中的接口標(biāo)準(zhǔn)。(電壓不一致)
  3. 我們?cè)O(shè)計(jì)了一個(gè)適配器,將接口標(biāo)準(zhǔn)改為我們需要的標(biāo)準(zhǔn)。(110V to 220V)
  4. 我們?cè)O(shè)計(jì)的適配器負(fù)責(zé)做接口轉(zhuǎn)換,如非必要并不改動(dòng)原有模塊的功能。(電源還是那個(gè)電源)

開發(fā)案例

作為移動(dòng)開發(fā)者,在開發(fā)中我們遇到的跟適配器有關(guān)的一個(gè)典型案例就是廣告SDK適配器。

在移動(dòng)開發(fā)領(lǐng)域,開發(fā)者為了獲取收益,往往會(huì)通過(guò)接廣告平臺(tái)的SDK來(lái)獲得廣告收益。

廣告平臺(tái)有很多,比如谷歌的Admob,騰訊的優(yōu)量匯,臉書的Audience Network等。

對(duì)于開發(fā)者來(lái)說(shuō),如果想在應(yīng)用中插入一個(gè)插屏廣告,那么從開發(fā)者角度來(lái)講,接口上來(lái)說(shuō)僅僅是調(diào)用一個(gè)顯示插屏廣告的接口即可,而無(wú)需關(guān)心該接口來(lái)自Admob還是哪一家廣告商。

然而,各大廣告商提供的SDK并不統(tǒng)一,如廣告引擎初始化,彈起全屏廣告等邏輯,都有各自的設(shè)計(jì)方案。這時(shí)候,我們就需要一個(gè)適配器,將各大平臺(tái)的廣告API適配成我們自己需要的接口。

從案例出發(fā),我們?cè)O(shè)計(jì)一個(gè)廣告展示抽象類AbstractAdsAdapter

class AbstractAdsAdapter {
    // 加載廣告
    func load() {}
    // 顯示廣告
    func showInterstitial() {}
}

通過(guò)抽象類,設(shè)計(jì)具體的子類AdmobAdsAdapter。

class AdmobAdsAdapter: AbstractAdsAdapter {
    override func initializeEngine() {
        print("調(diào)用Admob初始化引擎接口")
    }
    
    override func load() {
        print("調(diào)用Admob加載廣告API的接口")
    }
    
    override func showInterstitial() {
        print("調(diào)用Admob展示廣告API的接口")
    }
}

這樣,Admob的調(diào)用實(shí)現(xiàn)細(xì)節(jié)就通過(guò)AdmobAdsAdapter來(lái)完成了。

如果我們還接了廣點(diǎn)通的廣告,那么就設(shè)計(jì)新的子類TencentAdsAdapter。

class TencentAdsAdapter: AbstractAdsAdapter {
    override func initializeEngine() {
        print("調(diào)用廣點(diǎn)通初始化引擎接口")
    }
    
    override func load() {
        print("調(diào)用廣點(diǎn)通加載廣告API的接口")
    }
    
    override func showInterstitial() {
        print("調(diào)用廣點(diǎn)通展示廣告API的接口")
    }
}

接下來(lái),為了完成廣告模塊的調(diào)用,我們提供了一個(gè)廣告管理者類。

class AdsManager {
    enum Engine: String {
        case admob, tencent
    }
    private var map: [String: AbstractAdsAdapter] = [:]
    
    func initialize(_ engine: Engine) {
        var adapter: AbstractAdsAdapter?
        switch engine {
        case .admob :
            adapter = AdmobAdsAdapter()
        case .tencent :
            adapter = TencentAdsAdapter()
        }
        if adapter != nil {
            map[engine.rawValue] = adapter
            adapter!.initializeEngine()
        }
    }
    
    func load(engine: Engine) {
        if let adapter = map[engine.rawValue] {
            adapter.load()
        } else {
            print("廣告引擎 \(engine.rawValue) 未初始化")
        }
    }
    
    func showInterstitial(engine: Engine) {
        if let adapter = map[engine.rawValue] {
            adapter.showInterstitial()
        } else {
            print("廣告引擎 \(engine.rawValue) 未初始化")
        }
    }
}

該管理者類提供了一個(gè)枚舉類型Engine,當(dāng)我們指定對(duì)應(yīng)的引擎時(shí),管理者類就會(huì)調(diào)用對(duì)應(yīng)的廣告模塊。

測(cè)試代碼如下

class AdsManager {
    enum Engine: String {
        case admob, tencent
    }
    private var map: [String: AbstractAdsAdapter] = [:]
    
    func initialize(_ engine: Engine) {
        var adapter: AbstractAdsAdapter?
        switch engine {
        case .admob :
            adapter = AdmobAdsAdapter()
        case .tencent :
            adapter = TencentAdsAdapter()
        }
        if adapter != nil {
            map[engine.rawValue] = adapter
            adapter!.initializeEngine()
        }
    }
    
    func load(engine: Engine) {
        if let adapter = map[engine.rawValue] {
            adapter.load()
        } else {
            print("廣告引擎 \(engine.rawValue) 未初始化")
        }
    }
    
    func showInterstitial(engine: Engine) {
        if let adapter = map[engine.rawValue] {
            adapter.showInterstitial()
        } else {
            print("廣告引擎 \(engine.rawValue) 未初始化")
        }
    }
}

廣告管理者類通過(guò)一個(gè)容器map存放已經(jīng)初始化的廣告適配器,然后同樣提供和實(shí)現(xiàn)了加載和展示廣告的接口。如果一個(gè)廣告適配器未進(jìn)行初始化,則不會(huì)放入到map中,這時(shí)候調(diào)用其它接口將會(huì)導(dǎo)致失敗。

調(diào)用代碼測(cè)試。

let adsManager = AdsManager()
adsManager.initialize(.admob)
adsManager.load(engine: .admob)
adsManager.showInterstitial(engine: .admob)
adsManager.showInterstitial(engine: .tencent)

打印輸出。

調(diào)用Admob初始化引擎接口
調(diào)用Admob加載廣告API的接口
調(diào)用Admob展示廣告API的接口
廣告引擎 tencent 未初始化

廣告適配器的示例代碼到這里就差不多了,雖然實(shí)際項(xiàng)目開發(fā)中該框架還有不少要加入的,比如錯(cuò)誤處理等。但是整個(gè)模型依然是建立在適配器的基礎(chǔ)上。

以上示例代碼在我的github上可以下載,你可以戳這里,或者直接訪問(wèn):https://github.com/FengHaiTongLuo/Swift-DesignPattern/blob/main/Adapter.swift 。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容