適配器模式
當(dāng)項(xiàng)目中的埋點(diǎn)使用了友盟的SDK,哪一天,產(chǎn)品要求把友盟的SDK換成神策數(shù)據(jù)的,因?yàn)閮蓚€(gè)SDK的接口不一樣,如果要更換,那不是得替換掉很多代碼
比如以前使用友盟的接口是[UMMob eventId:@"123"],神策是[Analyze mobEventId:@"123"],因?yàn)槲覀冺?xiàng)目大量的使用了友盟了,所以這時(shí)候,我們可以新建一個(gè)類叫UMAdapter,類中的方法為eventId:,這樣我們就只需要替換UMMob類
那么這種形式就是適配器模式了,UMAdapter為適配器,eventId:為目標(biāo)接口,Analyze為要適配的類
看看具體的實(shí)現(xiàn)
新建一個(gè)類UMAdapter繼承Analyze,實(shí)現(xiàn)一個(gè)方法eventId:,方法的實(shí)現(xiàn)為
- (void)eventId:(NSString *)eventId
{
[self mobEventId:eventId];
}
這樣我們就可以使用UMAdapter類去替換友盟了,那么這種方式呢,就是最簡(jiǎn)單的適配器模式了
有人可能會(huì)說,適配器模式不是還得設(shè)計(jì)接口嗎,iOS上叫設(shè)計(jì)協(xié)議。
我們看下適配器模式的介紹
將一個(gè)類的接口轉(zhuǎn)換為客戶希望的另一個(gè)接口,它使得原來由于兼容問題不能一起工作的那些類可以一起工作。
我的理解,存在一個(gè)類的接口,不滿足我們使用,所以我們才使用適配器,例如剛才的神策接口,我們使用適配器,將其適配成類似友盟的接口
使用適配器還有條件,往往是適配出相似功能的才使用適配器,比如剛才的神策適配成友盟接口,功能都是埋點(diǎn),不能說將神策埋點(diǎn)的接口,適配成shareSDK,所以說是相似功能才進(jìn)行適配
適配器模式的角色有適配器,被適配者,目標(biāo)接口,在剛才的例子中適配器就是UMAdapter,被適配者是Analyze(神策SDK),目標(biāo)接口是eventId:,所以回到剛才的問題,為什么不需要設(shè)計(jì)接口,協(xié)議呢,這里要看對(duì)接口的理解
我的個(gè)人理解
提供給外部使用的方法,無論是協(xié)議的形式,抽象類虛函數(shù)的形式,甚至具體類的方法,只要是提供給外部使用的都可以叫接口
那么假如我要適配的接口有10個(gè),是一系列的,那么我要去優(yōu)化他,我就可以定義一個(gè)協(xié)議,把這堆方法放到這個(gè)協(xié)議中
我對(duì)協(xié)議使用場(chǎng)景的理解,有一個(gè)使用場(chǎng)景哦
協(xié)議可以放著同一系列功能的方法,這樣可以提高代碼的結(jié)構(gòu)化和可讀性,比把這堆方法直接寫到具體類中,然后再寫#pragma mark - 分段 好一點(diǎn) 常見的這種用法是定義一個(gè)協(xié)議,然后一個(gè)基類,基類里面實(shí)現(xiàn)協(xié)議,將協(xié)議的方法充當(dāng)虛函數(shù),經(jīng)??吹竭@種寫法,在適配器模式里面,但是我還不能很好的去理解這種操作,歡迎交流這點(diǎn)?。?!
剛才之所以直接寫到具體類中,是因?yàn)橹挥幸粋€(gè)方法,沒有必要去過度的設(shè)計(jì),還有剛才一開始那樣寫想表達(dá)的意思是,理解一種設(shè)計(jì)模式,應(yīng)該是先理解他是為了解決什么問題,怎么去解決的,然后具體代碼怎么做,而不是墨守成規(guī)的去遵守,一定要設(shè)計(jì)接口,一定要用繼承等等。
比如剛才的UMAdapter繼承Analyze
適配器繼承被適配器,這種模式叫類適配器
之所以要繼承,無非就是想在方法eventId:中直接調(diào)用Analyze類的方法,這樣簡(jiǎn)單的實(shí)現(xiàn)了轉(zhuǎn)換
那么除了這種方法,我們還可以不用繼承呀。在UMAdapter類中聲明Analyze類,在轉(zhuǎn)換接口中調(diào)用Analyze對(duì)象的mobEventId方法就可以啦,這種就是對(duì)象適配器模式
@interface UMAdapter : NSObject
@property (nonatomic, strong) Analyze *analyze;
@end
@implementation UMAdapter
- (void)eventId:(NSString *)eventId
{
[self.analyze mobEventId:eventId];
}
- (Analyze *)analyze
{
if (!_analyze)
{
_analyze = [Analyze alloc] init];
}
return _analyze;
}
@end
上面講了適配器,被適配器,接口,類適配模式,對(duì)象適配器模式,接下來再講最后一個(gè),缺省適配器模式
缺省就是默認(rèn) default的意思
在java中,如果你定義了接口
public interface InterfaceA {
public void fun1();
public void fun2();
public void fun3();
public void fun4();
public void fun5();
}
那么你想要實(shí)現(xiàn)這個(gè)接口,就得實(shí)現(xiàn)里面的所有方法,不然會(huì)報(bào)錯(cuò)
所以有個(gè)問題,比如在java里面,適配器A只需要實(shí)現(xiàn)fun1(),fun2(),fun3(),就可以了,適配器B只需要實(shí)現(xiàn)fun3(),fun4(),fun5()就可以了
那么如果要寫成適配器模式,在java里面,這種情況就得定義兩套接口,分別給適配器A和B用,那么這樣代碼肯定會(huì)有重復(fù),多余
那么缺省適配器模式就來了
定義一個(gè)抽象適配器類,實(shí)現(xiàn)接口InterfaceA,在里面實(shí)現(xiàn)接口InterfaceA的所有方法,并且給予默認(rèn)實(shí)現(xiàn),那么適配器A,適配器B就可以繼承抽象適配器類,分別去重寫想要的接口就可以了
這種就是缺省適配器模式
那么在iOS,接口一般是協(xié)議來搞,協(xié)議有可選和必選兩種,那么就沒有這種問題
適配器的應(yīng)用場(chǎng)景
被動(dòng)使用:就是本文的例子了
主動(dòng)使用:比如目前項(xiàng)目基于AFNetWorking設(shè)計(jì)網(wǎng)絡(luò)層的,但是我設(shè)計(jì)之初就想好,以后可能需要更換網(wǎng)絡(luò)請(qǐng)求框架,那么如果真的要更換了,那不是AFNetWording很多接口都得換,那么不就需要使用適配器模式了,但是被動(dòng)使用還不如主動(dòng)出擊,就是設(shè)計(jì)的時(shí)候就考慮好,以后可以隨意的切換不同的網(wǎng)絡(luò)框架,推薦猿題庫的一個(gè)框架YTKNetwork,這個(gè)主要設(shè)計(jì)就是考慮了這一點(diǎn)
總結(jié)
適配器模式角色,適配器,被適配器,源接口(要轉(zhuǎn)換的接口),目標(biāo)接口(外部想要的接口)
適配器有類適配器,對(duì)象適配器,缺省適配器,其實(shí)本質(zhì)上都是大同小異,比如類適配器和對(duì)象適配器,只是適配器->被適配器之間的關(guān)系不同而已,最后的目的都是在適配器中調(diào)到源接口
缺省適配器 就剛才的例子中,是java中實(shí)現(xiàn)接口必須全部實(shí)現(xiàn),所以才有這個(gè)由來,iOS就沒有這個(gè)問題