iOS開發(fā)適配器模式實戰(zhàn)

相信做App開發(fā)的同學,對于一些第三方的統(tǒng)計分析、錯誤收集等SDK應該都不陌生。就目前而言市面上也有許多相同功能的產品,眼花繚亂,讓人無法抉擇選哪一款SDK才是最靠譜的。那就隨便先選一款試試用吧!

那么問題來了:如果項目都快做完了結果發(fā)現(xiàn)這款SDK實在坑爹,不僅擴展性差,還經(jīng)常讓App Crash,那你是不是會想到替換掉這個SDK?

OK,那我們就換另一個試試,下載SDK下來,一看,傻眼了,設計風格,封裝模塊完全不一樣,于是乎我們就到項目中全局搜索找到之前的SDK代碼干掉,然后重新再到各種地方用新的SDK來寫新的邏輯來替換,關鍵的是,中間還不知道會產生多少bug,漏掉多少未修改的代碼,總之始終會有一種不靠譜的感覺。

換一次還算好的,如果之后團隊壯大了,這些數(shù)據(jù)分析之類的東西突然想自己做了,畢竟這些有價值的數(shù)據(jù)并不想這么拱手讓給一個第三方的公司嘛~這個時候你是不是就只想說:『呵呵』

所以這個時候適配器模式就起到作用了~

何為適配器模式

GoF對于適配器模式的解釋如下:

將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以在一起工作。

個人通俗理解:

適配器:顧名思義,將不兼容的轉換為兼容,如電源適配器,將全世界各種不相同的電壓轉換成相同的電壓輸出給目標設備。

這里可以將目標設備理解為『接口』,世界各種電壓可以理解為『產生相同功能的類』,電源適配器可以理解為『需要實現(xiàn)的適配器類』。

適配器模式產生的效果是:在不修改代碼或者修改極少代碼的情況下,快速的切換源(數(shù)據(jù)源、內容源等)。

就像電源適配器一樣,去到不同國家,同一個設備只需要不同的電源適配器就可以使用當前國家的電源,而不需要取拆卸機器。

使用真實場景

如文章開頭所講,被某盟的SDK坑了之后(確實在某些狀況下讓App Crash,產生原因初步判斷是濫用performSelector,不考慮對象被釋放的情況而產生的Crash),產生替換念想而思考,如果將來替換豈不是又要苦逼我們自己?

于是乎為了將來的輕松就必須動動腦子去設計代碼了,于是有了今天的適配器模式實戰(zhàn)。

懶的程序員才是好程序員,所以才會有所謂的『面向對象』,所謂的『設計模式』。

代碼

首先我們先定義好目標接口,在iOS也就是Protocol

針對統(tǒng)計分析這一個大模塊,我先設計了KTRUserAnalysisProtocol、KTRUserEvent、KTRAutoUpdate等幾個協(xié)議,通過這些協(xié)議我們可以實現(xiàn)我們想要的功能。

KTRAutoUpdate中,定義的方法如下:

/**
 *  檢查更新
 *
 *  @param ower     擁有者,用于判斷擁有者是否存在
 *  @param finished 檢查更新完成后回調函數(shù)
 *  @param result   是否為自動檢查(若為自動檢查則需要檢查網(wǎng)絡環(huán)境)
 *  @param isSilent 是否靜默方式檢查更新
 */
- (void)checkUpdateWith:(id)ower isAuto:(BOOL)result isSilent:(BOOL)isSilent callBack:(KTRUserAnalysisVoidBlock)finished;

- (void)checkUpdateWith:(id)ower isAuto:(BOOL)result callBack:(KTRUserAnalysisVoidBlock)finished;

- (void)checkUpdateWith:(id)ower callBack:(KTRUserAnalysisVoidBlock)finished;
/**
 *  判斷是否有更新
 *
 *  @return 當checkUpdateWith檢查完畢之后,一般情況會保存結果到UserDefault中,在后續(xù)其他UI中可以直接使用而不必網(wǎng)絡檢查
 */
- (BOOL)appIsNeedUpdate;

通過這些方法我就可以進行各種類型的更新檢測了。

定義好Protocol之后需要做的就是創(chuàng)建目標基類(抽象類),猶豫Objective-C沒有抽象類的概念所以,只能自己使用特殊的方法來代替,那就是在方法體中使用:


- (void)checkUpdateWith:(id)ower isAuto:(BOOL)result isSilent:(BOOL)isSilent callBack:(KTRUserAnalysisVoidBlock)finished
{
    NSAssert(NO, @"具體適配器必須實現(xiàn)該方法");
}

這樣子類在沒有重載方法而調用該方法的時候就會報運行時錯誤,從而強制其實現(xiàn)。

緊接著就是實現(xiàn)具體的Adapter了,在這個項目中我命名為KTRUMAdapter

實現(xiàn)過程沒有什么太多需要講的,因為各自的業(yè)務不一樣,實現(xiàn)也就不一樣沒有什么可參考性,但是這里,有一點是比較好玩的,也就之解決前說的UM的SDK沒有考慮對象釋放而產生的Crash。


- (void)checkUpdateWith:(id)ower isAuto:(BOOL)result isSilent:(BOOL)isSilent callBack:(KTRUserAnalysisVoidBlock)finished
{
    self.updateOwer = ower;
    self.updateCallback = finished;
    //判斷是否wifi環(huán)境
    if ([self networkStatus] == ReachableViaWiFi || !result)
    {
        //如果為靜默方式檢查,則調用appSilentUpdate
        if (isSilent) {
            [MobClick cancelPreviousPerformRequestsWithTarget:self selector:@selector(appSilentUpdate:) object:nil];
            [MobClick checkUpdateWithDelegate:self selector:@selector(appSilentUpdate:)];
            return;
        }
        
        self.checkUpdateTips = YES;
        __weak typeof (self)weakSelf = self;
        //如果友盟檢測更新10秒后無響應則自動回調
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (weakSelf.checkUpdateTips)
            {
                //取消請求
                [MobClick cancelPreviousPerformRequestsWithTarget:self selector:@selector(appUpdate:) object:nil];
                if(finished)
                {
                    finished();
                }
                weakSelf.checkUpdateTips = NO;
            }
        });

        [MobClick checkUpdateWithDelegate:self selector:@selector(appUpdate:)];
    }
    else
    {
        self.updateCallback = finished;
    }
}

/**
 *  友盟檢查更新回調函數(shù)
 *
 *  @param appInfo
 */
- (void)appUpdate:(NSDictionary *)appInfo
{
    self.checkUpdateTips = NO;
    if (self.updateOwer)
    {
        [self alertUpdateView:appInfo];
        if (self.updateCallback)
        {
            self.updateCallback();
        }
    }
}

在設計的時候我使用單例模式來實現(xiàn)的Adapter,為的就是在App運行過程中不被釋放,這樣就可以保證UM的回調不會因為找不到對象而回調方法Crash,然后在單例里面判斷調用該方法的對象是否還存在,存在則繼續(xù)回調,如果不存在則略過當前方法。

在iOS中其實是沒有必要創(chuàng)建這個抽象類的,直接使用Protocol然后用delegate的方式也就能夠完成基類的工作。

但是為了更加方便給客戶端使用,抽象類是一個不錯的選擇,因為在客戶端只需要如此使用:


KTRUserAnalysis *userAnalysis = [KTRUserAnalysis sharedInstanceWithAdapter:[[KTRUMAdapter alloc]init]];

BOOL result = [[KTRUserAnalysis sharedInstance] appIsNeedUpdate];

后續(xù)如果要換掉Adapter,就僅僅改掉此處就皆大歡喜了。當然前提是,先要實現(xiàn)另一個Adapter。

總結

適配器模式的有點很多,如:將客戶端與適配者解耦、使客戶端調用更加簡明(只需要調用定義的接口方法)。但同樣也有缺點,在寫適配器的時候為適應目標接口,可能會有比較復雜實現(xiàn)過程;無端端多出許多的類,使結構又復雜了一些。

所以在使用的時候需要因情況而定,不要生搬硬套盲目的使用設計模式。

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容