iOS中MVP架構(gòu)實踐小技巧

一般來說,MVP架構(gòu)在Andriod中用的比較多,但它也可以在iOS中使用。我在重構(gòu)項目的一個功能時,為了改善以前代碼的層次結(jié)構(gòu),同時也想體驗一下MVP的實踐,所以使用了該模式,同時也積累了一點小技巧。

MVP分層模型以及交互關(guān)系如圖所示:

QQ20181113-1.png

view和model通過presenter進行交互,切斷直接聯(lián)系。

在使用該架構(gòu)后,雖然分層清晰了,但是它有個缺點,presenter中粘合接口過多。

我們知道,mvp各層的交互都是通過接口來完成的,presenter作為中介者,需要實現(xiàn)view操作model層的接口,model層操作UI的接口。而presenter實現(xiàn)這些接口時,大部分是簡單的調(diào)用model和view的接口,并沒有其他額外操作,這樣會導(dǎo)致presenter中粘合方法過多,并且新增接口,presenter也需要新增實現(xiàn)。所以,當(dāng)功能復(fù)雜時,接口暴增,presenter中也會有越來越多的接口實現(xiàn),同時也不利于維護。

先看個簡單的彈幕例子,介紹下上面所說的問題。

Interface

// presenter提供的給view調(diào)用的接口
@protocol DanmuPresenterInterface <NSObject>
@optional
/// 清除聊天記錄
- (void)cleanChats;
@end
// view提供的給presenter調(diào)用的接口
@protocol DanmuViewInterface <NSObject>

@optional
// reload
- (void)reloadTableView;
@end
// model層調(diào)用presenter,更新ui接口
@protocol DanmuDataOutputInterface <NSObject>

@optional
// reload
- (void)reloadTableView;
@end
// prenseter調(diào)用model層,更新數(shù)據(jù)接口
@protocol DanmuDataInterface <NSObject>
@optional
/// 清除聊天記錄
- (void)cleanChats;
@end

Presenter

@interface DanmuPresenter()
// 數(shù)據(jù)層接口
@property (nonatomic, strong) id<DanmuDataInterface> dataManager;
// ui層接口
@property (nonatomic, weak) id<DanmuViewInterface> danmuViewInterface;
@end

@implementation DanmuPresenter

// presenter提供的給view調(diào)用的接口
#pragma mark - DanmuPresenterInterface
/// 清除聊天記錄
- (void)cleanChats {
    [self.dataManager cleanChats];
}

// 實現(xiàn)model層調(diào)用更新ui接口
#pragma mark - DanmuDataOutputInterface
// reload
- (void)reloadTableView {
    [self.danmuViewInterface reloadTableView];
}

@end

這個例子中,交互關(guān)系如下:

QQ20181113-2.png

在view中的調(diào)用如下:

// self.presenterInterface為presenter
[self.presenterInterface cleanChats];

在dataManager中調(diào)用如下:

// self.DanmuDataOutputInterface為presenter
[self.DanmuDataOutputInterface reloadTableView];

從上面可以看出,如果DanmuPresenterInterface、DanmuDataOutputInterface有新增接口,presenter中必須新增相應(yīng)實現(xiàn),比較麻煩。

實際上,在danmuView中調(diào)用cleanChats時,presenter只是起了一層中轉(zhuǎn)的作用,內(nèi)部還是直接調(diào)用的dataManager的接口。對于這種類型的接口來說,會極大的增加presenter的接口實現(xiàn)方法數(shù)。

所以,在重構(gòu)過程中,為了減少粘合接口,考慮直接將消息轉(zhuǎn)發(fā)到對應(yīng)的實例中,不需要寫實現(xiàn)方法。如下所示。

  • 如果是danmuView通過DanmuPresenterInterface接口(最后實際上是調(diào)用DanmuDataInterface操作model數(shù)據(jù)),則直接轉(zhuǎn)發(fā)到dataManager
  • 如果是dataManager調(diào)用DanmuDataOutputInterface接口來更新UI,則直接轉(zhuǎn)發(fā)到danmuView。
// 由于presenter作為中介者,需要實現(xiàn)view操作model層的接口(具體實現(xiàn)為dataManger),model層操作UI的接口(具體實現(xiàn)為chatView),這樣會導(dǎo)致粘合方法過多,并且新增接口,presenter也需要新增實現(xiàn)。故使用消息轉(zhuǎn)發(fā)來簡化處理。
- (id)forwardingTargetForSelector:(SEL)aSelector {
    // 轉(zhuǎn)發(fā)DanmuDataInterface實現(xiàn)到dataManager
    struct objc_method_description omd = protocol_getMethodDescription(@protocol(DanmuDataInterface), aSelector, NO, YES);
    if (omd.name != NULL) {
        if ([self.dataManager respondsToSelector:aSelector]) {
            return self.dataManager;
        }
    }
    
    // 轉(zhuǎn)發(fā)DanmuDataOutputInterface實現(xiàn)到danmuView
    omd = protocol_getMethodDescription(@protocol(DanmuDataOutputInterface), aSelector, NO, YES);
    if (omd.name != NULL) {
        if ([self.danmuViewInterface respondsToSelector:aSelector]) {
            return self.danmuViewInterface;
        }
    }
    
    return [super forwardingTargetForSelector:aSelector];
}

這樣,DanmuDataInterface、DanmuDataOutputInterface中的接口在presenter中的實現(xiàn)均可去除。在dataManager調(diào)用的地方為[self.uiInterface reloadTableView],注意這里不能判斷respondsToSelector,因為presenter并沒有實現(xiàn)這些方法,所以判斷了不會走。

但是,這種做法是有限制的。要求presenter中實現(xiàn)的接口,是沒有做任何額外的邏輯,而是直接調(diào)用model層或者ui層的實現(xiàn)。

比如,下面的實現(xiàn)另外調(diào)用了[self xx],就不適用了。

#pragma mark - DanmuPresenterInterface
/// 清除聊天記錄
- (void)cleanChats {
    // do something
    [self xx];
    [self.dataManager cleanChats];
}

以上,就是mvp實踐過程的小結(jié)。

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

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,057評論 25 709
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,000評論 2 59
  • 前言 看了下上篇博客的發(fā)表時間到這篇博客,竟然過了11個月,罪過,罪過。這一年時間也是夠折騰的,年初離職跳槽到鵝廠...
    西木柚子閱讀 21,425評論 12 183
  • 離蕭閱讀 357評論 0 0
  • 染上瘧疾,又死里逃生,父母回日本照顧患病的長輩,弟弟因找不著朋友而悶悶不樂,外婆外公為了維持生計必須要收割麥子,自...
    簡翼閱讀 317評論 0 0

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