iOS中MVP架構實踐小技巧

通常來講,MVP架構在Andriod中用的比較多,但它也能夠在iOS中使用。我在重構項目的一個功能時,爲了改善之前代碼的層次結構,同時也想體驗一下MVP的實踐,因此使用了該模式,同時也積累了一點小技巧。bash

MVP分層模型以及交互關係如圖所示:架構

QQ20181113-1.png

view和model經過presenter進行交互,切斷直接聯繫。ui

在使用該架構後,雖然分層清晰了,可是它有個缺點,presenter中粘合接口過多atom

咱們知道,mvp各層的交互都是經過接口來完成的,presenter做爲中介者,須要實現view操做model層的接口model層操做UI的接口。而presenter實現這些接口時,大部分是簡單的調用model和view的接口,並無其餘額外操做,這樣會致使presenter中粘合方法過多,而且新增接口,presenter也須要新增實現。因此,當功能複雜時,接口暴增,presenter中也會有愈來愈多的接口實現,同時也不利於維護。spa

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

Interfacecdn

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

@optional
// reload
- (void)reloadTableView;
@end
複製代碼
// model層調用presenter,更新ui接口
@protocol DanmuDataOutputInterface <NSObject>

@optional
// reload
- (void)reloadTableView;
@end
複製代碼
// prenseter調用model層,更新數據接口
@protocol DanmuDataInterface <NSObject>
@optional
/// 清除聊天記錄
- (void)cleanChats;
@end
複製代碼

Presenterblog

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

@implementation DanmuPresenter

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

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

@end
複製代碼

這個例子中,交互關係以下:接口

QQ20181113-2.png

在view中的調用以下:ip

// self.presenterInterface爲presenter
[self.presenterInterface cleanChats];
複製代碼

在dataManager中調用以下:

// self.DanmuDataOutputInterface爲presenter
[self.DanmuDataOutputInterface reloadTableView];
複製代碼

從上面能夠看出,若是DanmuPresenterInterface、DanmuDataOutputInterface有新增接口,presenter中必須新增相應實現,比較麻煩。

實際上,在danmuView中調用cleanChats時,presenter只是起了一層中轉的做用,內部仍是直接調用的dataManager的接口。對於這種類型的接口來講,會極大的增長presenter的接口實現方法數。

因此,在重構過程當中,爲了減小粘合接口,考慮直接將消息轉發到對應的實例中,不須要寫實現方法。以下所示。

  • 若是是danmuView經過DanmuPresenterInterface接口(最後其實是調用DanmuDataInterface操做model數據),則直接轉發到dataManager
  • 若是是dataManager調用DanmuDataOutputInterface接口來更新UI,則直接轉發到danmuView
// 因爲presenter做爲中介者,須要實現view操做model層的接口(具體實現爲dataManger),model層操做UI的接口(具體實現爲chatView),這樣會致使粘合方法過多,而且新增接口,presenter也須要新增實現。故使用消息轉發來簡化處理。
- (id)forwardingTargetForSelector:(SEL)aSelector {
    // 轉發DanmuDataInterface實現到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;
        }
    }
    
    // 轉發DanmuDataOutputInterface實現到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中的實現都可去除。在dataManager調用的地方爲[self.uiInterface reloadTableView],注意這裏不能判斷respondsToSelector,由於presenter並無實現這些方法,因此判斷了不會走。

可是,這種作法是有限制的。要求presenter中實現的接口,是沒有作任何額外的邏輯,而是直接調用model層或者ui層的實現。

好比,下面的實現另外調用了[self xx],就不適用了。

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

以上,就是mvp實踐過程的小結。

相關文章
相關標籤/搜索