MVC扯饶,MVP恒削,MVVM 拾遺

做一個(gè)用戶登錄中心池颈,那么首先。钓丰。躯砰。

  • 使用何種設(shè)計(jì)模式 ,MVC 携丁,MVP 琢歇,MVVM?

為什么第一個(gè)問題要關(guān)注選擇何種設(shè)計(jì)模式 ,因?yàn)槿绻悴魂P(guān)心設(shè)計(jì)模式梦鉴,慢慢的你會發(fā)現(xiàn)UserCenterViewController 里面的代碼會滿滿積累李茫,最終變成一個(gè)Massive ViewController ,想快速的修bug 或者增加功能變得異常麻煩。

Screen Shot 2017-03-24 at 12.01.17 AM.png

今天談?wù)摰脑O(shè)計(jì)模式

  • MVC
  • MVP
  • MVVM

三者的共同點(diǎn)表展在Model 和 View
Models : 負(fù)責(zé)數(shù)據(jù)的操作。
View : UI 展示,沒什么好說的枚驻。

三者的差異主要體現(xiàn)在粘合View和Model的方式:
1.Controller接收View的操作事件首妖,根據(jù)事件不同,或者調(diào)用Model的接口進(jìn)行數(shù)據(jù)操作塔次,或者進(jìn)行View的跳轉(zhuǎn)方篮,從而也意味著一個(gè)Controller可以對應(yīng)多個(gè)View。Controller對View的實(shí)現(xiàn)不太關(guān)心励负,只會被動地接收藕溅,Model的數(shù)據(jù)變更不通過Controller直接通知View,通常View采用觀察者模式監(jiān)聽Model的變化继榆。

2.Presenter巾表,與Controller一樣,接收View的命令略吨,對Model進(jìn)行操作集币;與Controller不同的是Presenter會反作用于View,Model的變更通知首先被Presenter獲得翠忠,然后Presenter再去更新View鞠苟。一個(gè)Presenter只對應(yīng)于一個(gè)View。根據(jù)Presenter和View對邏輯代碼分擔(dān)的程度不同秽之,這種模式又有兩種情況:Passive View和Supervisor Controller当娱。

3.ViewModel,注意這里的“Model”指的是View的Model考榨,跟上面那個(gè)Model不是一回事跨细。所謂View的Model就是包含View的一些數(shù)據(jù)屬性和操作的這么一個(gè)東東,這種模式的關(guān)鍵技術(shù)就是數(shù)據(jù)綁定(data binding)河质,View的變化會直接影響ViewModel冀惭,ViewModel的變化或者內(nèi)容也會直接體現(xiàn)在View上震叙。這種模式實(shí)際上是框架替應(yīng)用開發(fā)者做了一些工作,開發(fā)者只需要較少的代碼就能實(shí)現(xiàn)比較復(fù)雜的交互云头。

Coding time


MVC

Model:

@interface AccountInfo : NSObject
@property (nonatomic,copy) NSString *userName;
@property (nonatomic,copy) NSString *mobile;
@property (nonatomic,copy) NSString *avatarUrl;
@end

Controller & View:

@interface UserCenterViewController ()
@property (nonatomic,strong) UILabel *userDescLabel;
@property (nonatomic,strong) UIButtom *renameButton;
@property (nonatomic,strong) AccountInfo *accountInfo;
@end

@implementation UserCenterViewController

- (instancetype)initWithAccountInfo:(AccountInfo *)accountInfo
{
    self = [super init];
    if (self)
    {
        self.accountInfo = accountInfo;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.userDescLabel];

    [self.renameButton addTarget:self action:@selector(renameAction) forControlEvents:UIControlEventTouchUpInside] ;
}

- (void)renameAction
{
      self.accountInfo.userName = @"vedon";
      self.userDescLabel.text = [NSString stringWithFormat:@"%@ %@",self.accountInfo.userName,self.accountInfo.mobile];

}
@end

調(diào)用方法:

AccountInfo *accountInfo = [AccountInfo new];
UserCenterViewController *userCenterController = [[UserCenterViewController alloc] initWithAccountInfo:accountInfo];
 [self presentViewController:userCenterController animated:YES completion:nil];

一開始所有的controller 都是如此純潔捐友,隨著需求迭代,你懂的溃槐。匣砖。。很早之前就有一篇文章提出了這個(gè)問題昏滴。MVC 很容易寫出很臃腫的Controller 猴鲫,但是它有一個(gè)很明顯的優(yōu)點(diǎn),開發(fā)效率高谣殊!

如何避臃腫拂共?

  • 盡量避免在Controller 的view上添加過多的view.可以把有復(fù)雜層次結(jié)構(gòu)的view抽離成一層contentView,提高復(fù)用率姻几。
  • 在controller 中寫業(yè)務(wù)邏輯代碼宜狐,要注意抽離成單獨(dú)的小模塊,形成復(fù)用模塊蛇捌。
  • 動畫代碼抽離到單獨(dú)的模塊抚恒。

提高代碼的復(fù)用率,帶來的好處可能是意想不到的络拌。在開發(fā)夸克瀏覽器的時(shí)候俭驮,云同步底層是直接拿UC瀏覽器的,不需要任何改動就可以使用了春贸。


MVP

在MVP 里面,UIViewController 實(shí)際上當(dāng)View來使用了混萝,Presenters充當(dāng)了Controller的角色。這種方式提高了可測試性萍恕,但是付出的代價(jià)是開發(fā)效率降低了逸嘀。

Model:

@interface AccountInfo : NSObject
@property (nonatomic,copy) NSString *userName;
@property (nonatomic,copy) NSString *mobile;
@end

Presenter:

@interface UserCenterPresenter : NSObject<UserCenterViewProtocol>
@property (nonatomic,weak) id<UserCenterViewProtocol> view;
@property (nonatomic,strong) AccountInfo *accountInfo;
@end


@implementation Presenter
- (void)showUserInfo
{
     NSString *desc = [NSString stringWithFormat:@"%@ %@",self.accountInfo.userName,self.accountInfo.mobile]
     [self.view showUserInfoWithDesc:desc];
}

- (void)renameAction
{
      self.accountInfo.userName = @"vedon";
     [self showUserInfo];
}
@end

Protocol:

@protocol UserCenterViewProtocol <NSObject>
- (void)showUserInfoWithDesc:(NSString *)desc;
@end

View/Controller:

@interface UserCenterViewController ()
@property (nonatomic,strong) UILabel *userDescLabel;
@property (nonatomic,strong) UIButtom *renameButton;
@property (nonatomic,strong) UserCenterPresenter *presenter;
@end

@implementation UserCenterViewController

- (instancetype)initWithAccountInfo:(AccountInfo *)accountInfo presenter:(UserCenterPresenter *)presenter
{
    self = [super init];
    if (self)
    {
        self.presenter = presenter;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.userDescLabel];
    [self.renameButton addTarget:self.presenter action:@selector(renameAction) forControlEvents:UIControlEventTouchUpInside] ;
}

#pragma mark  - UserCenterViewProtocol

- (void)showUserInfoWithDesc:(NSString *)desc
{
    self.userDescLabel.text = desc;
}

@end

調(diào)用方法:

AccountInfo *accountInfo = [AccountInfo new];
UserCenterPresenter *presenter = [UserCenterPresenter new];
presenter.accountInfo = accountInfo;
UserCenterViewController *userCenterController = [[UserCenterViewController alloc] initWithAccountInfo:accountInfo presenter:presenter];   
 presenter.view = userCenterController;
 [self presentViewController:userCenterController animated:YES completion:nil];

MVP從更早的MVC框架演變過來的一種框架,與MVC有一定的相似性雄坪。MVP框架由3部分組成:View負(fù)責(zé)顯示厘熟,Presenter負(fù)責(zé)邏輯處理(一個(gè)Presenter只對應(yīng)于一個(gè)View或者是一類view),Model提供數(shù)據(jù)维哈。MVP與MVC之間最主要的區(qū)別在控制層上绳姨,在MVP框架中,View與Model并不直接交互阔挠,所有的交互放在Presenter中飘庄;而在MVC里,View與Model會直接產(chǎn)生一定的交互购撼。MVP的Presenter是框架的控制者跪削,承擔(dān)了大量的邏輯操作谴仙,而MVC的Controller更多時(shí)候承擔(dān)一種轉(zhuǎn)發(fā)的作用。因此在App中引入MVP的原因碾盐,是為了將此前在Controller中包含的大量邏輯操作放到控制層中晃跺,避免Controller的臃腫。

現(xiàn)在假如我們通過上面的方式解決了部分C太重的問題毫玖,我們看上圖Presenter涉及到的調(diào)用非常復(fù)雜掀虎,這幾個(gè)交互可以說是業(yè)務(wù)代碼的bug聚集地,單向的數(shù)據(jù)流能規(guī)避很多問題付枫。


MVVM


Model :

@interface AccountInfo : NSObject
@property (nonatomic,copy) NSString *userName;
@property (nonatomic,copy) NSString *mobile;

Protocol:

@protocol UserCenterViewModelProtocol <NSObject>
- (void)renameAction;
@end

ViewModel:

typedef  void (^UserInfoDidChangeCallback)(NSString *desc);

@interface UserCenterViewModel : NSObject<UserCenterViewProtocol>
@property (nonatomic,weak) id<UserCenterViewProtocol> view;
@property (nonatomic,strong) AccountInfo *accountInfo;
@property (nonatomic,strong) UserInfoDidChangeCallback userInfoDidChangeCallback;

- (void)showUserInfo;

@end

@implementation UserCenterViewModel

- (void) renameAction
{
      self.accountInfo.userName = @"vedon";
      if(self.userInfoDidChangeCallback)
    {
         NSString *desc = [NSString stringWithFormat:@"%@ %@",self.accountInfo.userName,self.accountInfo.mobile]
          self. userInfoDidChangeCallback(desc)  
    }
}
@end

Controller:

@interface UserCenterViewController ()
@property (nonatomic,strong) UILabel *userDescLabel;
@property (nonatomic,strong) UIButtom *renameButton;
@property (nonatomic,strong) UserCenterViewModel *viewModel;
@end

@implementation UserCenterViewController

- (instancetype)initWithAccountInfo:(AccountInfo *)accountInfo viewModel:(UserCenterViewModel *)viewModel
{
    self = [super init];
    if (self)
    {
        self.viewModel = viewModel;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.userDescLabel];
    self.viewModel. userInfoDidChangeCallback = ^(NSString *desc){
         self.userDescLabel.text = desc;
    };

    [self.renameButton addTarget:self.viewModel action:@selector(renameAction) forControlEvents:UIControlEventTouchUpInside] ;
}


@end

MVP 和MVVM 非常相似烹玉!事實(shí)上,MVVM的View要比MVP中的View承擔(dān)的責(zé)任多阐滩。因?yàn)榍罢咄ㄟ^ViewModel的設(shè)置綁定來更新狀態(tài)二打,而后者只監(jiān)聽Presenter的事件但并不會對自己有什么更新。MVVM 和MVP帶來的好處非常明顯掂榔,它非常清晰的拆分開UI層和數(shù)據(jù)層 继效,使單元測試更加容易。在復(fù)雜的表示邏輯下装获,優(yōu)點(diǎn)會越發(fā)明顯莲趣!

對于ViewModel 的角色定位一定要清晰,它的存在是為了抽離ViewController 中的展示業(yè)務(wù)邏輯饱溢,視圖操作邏輯依然是在Controller 實(shí)現(xiàn)。那么在它里面就不應(yīng)該出現(xiàn)Push/Present 以及其他UIKit 的東西走芋。而展示業(yè)務(wù)邏輯是什么绩郎?就是負(fù)責(zé)將數(shù)據(jù)業(yè)務(wù)層提供的數(shù)據(jù)轉(zhuǎn)化為界面展示所需要,Model的信息可能并不能正確體現(xiàn)View狀態(tài)翁逞,所以需要經(jīng)過ViewModel的轉(zhuǎn)化肋杖。

MMVM 的關(guān)鍵在與數(shù)據(jù)綁定,查了一下挖函,已有開源框架在objc 的基礎(chǔ)上實(shí)現(xiàn)了一套數(shù)據(jù)綁定的框架状植。

題外話: 業(yè)務(wù)拆分

無論采取何種模式開發(fā),業(yè)務(wù)邏輯就在那里怨喘,不多不少津畸。ViewController 使用category 的形式進(jìn)行拆分。這樣一來必怜,Massive View Controller

此外肉拓,UI 組件設(shè)置樣式的代碼也可以通過 Category 的方法拆出去了。例如 :UILabel + PriceStyle 這個(gè) Category 里梳庆,UIButton 也是一樣的處理方式暖途。在使用的時(shí)候卑惜,直接通過 Category 里的方法獲取實(shí)例即可.

張三豐教無忌太極劍法,教完了卻讓他忘掉驻售,這是為什么呢
對話好像是這樣:“還記得嗎露久?”
“全都記得∑劾酰”
“現(xiàn)在呢毫痕?”
“已經(jīng)忘卻了一小半≈较铮”
“啊镇草,已經(jīng)忘了一大半×鲋迹”
“不壞不壞梯啤,忘得真快,那么現(xiàn)在呢存哲?”
“已經(jīng)全都忘了因宇,忘得干干凈凈∷钔担”
“好了察滑,你上吧⌒蕹Γ”
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贺辰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嵌施,更是在濱河造成了極大的恐慌饲化,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吗伤,死亡現(xiàn)場離奇詭異吃靠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)足淆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門巢块,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巧号,你說我怎么就攤上這事族奢。” “怎么了裂逐?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵歹鱼,是天一觀的道長。 經(jīng)常有香客問我卜高,道長弥姻,這世上最難降的妖魔是什么南片? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮庭敦,結(jié)果婚禮上疼进,老公的妹妹穿的比我還像新娘。我一直安慰自己秧廉,他們只是感情好伞广,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疼电,像睡著了一般嚼锄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蔽豺,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天区丑,我揣著相機(jī)與錄音,去河邊找鬼修陡。 笑死沧侥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的魄鸦。 我是一名探鬼主播宴杀,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拾因!你這毒婦竟也來了旺罢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤绢记,失蹤者是張志新(化名)和其女友劉穎主经,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庭惜,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年穗酥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了护赊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砾跃,死狀恐怖骏啰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抽高,我是刑警寧澤判耕,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站翘骂,受9級特大地震影響壁熄,放射性物質(zhì)發(fā)生泄漏帚豪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一草丧、第九天 我趴在偏房一處隱蔽的房頂上張望狸臣。 院中可真熱鬧,春花似錦昌执、人聲如沸烛亦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煤禽。三九已至,卻和暖如春岖赋,著一層夾襖步出監(jiān)牢的瞬間檬果,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工贾节, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汁汗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓栗涂,卻偏偏與公主長得像知牌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子斤程,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容