前言
之前在自己公司開(kāi)發(fā)過(guò)程之中,一直就是想辦法把代碼寫(xiě)的漂亮,可復(fù)用度高,就是不斷地稠代碼,分模塊.具體怎么做
寫(xiě)出比較完美的代碼,自己也不知道,現(xiàn)在有時(shí)間整理一些自己學(xué)習(xí)的心得,廢話不多說(shuō),先飛一波!

在開(kāi)始之前還是想講一下,代碼的規(guī)范
- 先是life cycle,即controller的生命周期等一些方法如:
#pragma mark - lifecycle
- (void)viewDidLoad
- 然后是 private method,一些私有方法(雖然說(shuō)好的代碼,控制器里面沒(méi)有私有方法,不過(guò)畢竟不是超級(jí)大神),即你自己封裝的一些功能類(lèi)方法
#pragma mark - private method
- (void)setupRefreshView
- 然后是Delegate方法實(shí)現(xiàn),先系統(tǒng)代理,然后自己寫(xiě)的代理
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
#pragma mark - SegmentCellDelegate
- (void)segmentClickedWithIndex:(NSInteger)selectedIndex
- 然后是event response,所有button、gestureRecognizer的響應(yīng)事件都放在這個(gè)區(qū)域里面,不要到處亂放。
#pragma mark - event response
//點(diǎn)擊空白處釋放鍵盤(pán)
- (void)tap
//點(diǎn)擊退出按鈕
-(void)existButton:(UIButton *)button
- 然后才是getters and setters,和 懶加載
#pragma mark - Getters and Setters
- (NSMutableArray *)searchSections {
if (!_searchSections) {
_searchSections = [[NSMutableArray alloc] init];
}
return _searchSections;
}
為什么要這樣要求?
我見(jiàn)過(guò)無(wú)數(shù)ViewController,代碼布局亂得一塌糊涂,這里一個(gè)delegate那里一個(gè)getter,然后ViewController的代碼一般都死長(zhǎng)死長(zhǎng)的,看了就讓人頭疼。
定義好這個(gè)規(guī)范,就能使得ViewController條理清晰,業(yè)務(wù)方程序員很能夠區(qū)分哪些放在ViewController里面比較合適,哪些不合適。另外,也可以提高代碼的可維護(hù)性和可讀性。
好了,希望以上可以幫到大家,這也是我看了一些文章整理出來(lái)常用 mark分類(lèi)方法.下面開(kāi)始主題了,一直聽(tīng)別人說(shuō)關(guān)于胖瘦Model的事,也不是很了解,下面我們
來(lái)扒一下他們的老底.是不是有些期待呢....
一 丶關(guān)于胖Model和瘦Model
- 什么叫胖Model?
胖Model包含了部分弱業(yè)務(wù)邏輯。胖Model要達(dá)到的目的是:Controller從胖Model這里拿到數(shù)據(jù)之后,不用額外做操作或者只要做非常少的操作,就能夠?qū)?shù)據(jù)直接應(yīng)用在View上.
FatModel:
@property (nonatomic, copy) NSString *calId; //日程id
@property (nonatomic, copy) NSString *title; //日程標(biāo)題
/*
* 判斷自己是否是該日程參與者
*/
+ (BOOL)isCalendarActory:(NSString *)doneUsers;
Controller:
BOOL ret = [FatModel isCalendarActory:@“”];
其優(yōu)點(diǎn)是:
這屬于業(yè)務(wù)代碼,算是弱業(yè)務(wù)。FatModel做了這些弱業(yè)務(wù)之后,Controller就能變得非常skinny,Controller只需要關(guān)注強(qiáng)業(yè)務(wù)代碼就行了。眾所周知,強(qiáng)業(yè)務(wù)變動(dòng)的可能性要比弱業(yè)務(wù)大得多,弱業(yè)務(wù)相對(duì)穩(wěn)定,所以弱業(yè)務(wù)塞進(jìn)Model里面是沒(méi)問(wèn)題的。
另一方面,弱業(yè)務(wù)重復(fù)出現(xiàn)的頻率要大于強(qiáng)業(yè)務(wù),對(duì)復(fù)用性的要求更高,如果這部分業(yè)務(wù)寫(xiě)在Controller,類(lèi)似的代碼會(huì)灑得到處都是,一旦弱業(yè)務(wù)有修改(弱業(yè)務(wù)修改頻率低不代表就沒(méi)有修改),這個(gè)事情就是一個(gè)災(zāi)難。如果塞到Model里面去,改一處很多地方就能跟著改,就能避免這場(chǎng)災(zāi)難。
其缺點(diǎn)是:
胖Model相對(duì)比較難移植,雖然只是包含弱業(yè)務(wù),但好歹也是業(yè)務(wù),遷移的時(shí)候很容易拔出蘿卜帶出泥。另外一點(diǎn),MVC的架構(gòu)思想更加傾向于Model是一個(gè)Layer,而不是一個(gè)Object,不應(yīng)該把一個(gè)Layer應(yīng)該做的事情交給一個(gè)Object去做。最后一點(diǎn),軟件是會(huì)成長(zhǎng)的,F(xiàn)atModel很有可能隨著軟件的成長(zhǎng)越來(lái)越Fat,最終難以維護(hù)。
- 2.什么叫瘦Model?
瘦Model只負(fù)責(zé)業(yè)務(wù)數(shù)據(jù)的表達(dá),所有業(yè)務(wù)無(wú)論強(qiáng)弱一律扔到Controller。ThinModel要達(dá)到的目的是,盡一切可能去編寫(xiě)細(xì)粒度Model,然后配套各種helper類(lèi)或方法來(lái)對(duì)弱業(yè)務(wù)做抽象,強(qiáng)業(yè)務(wù)依舊交給Controller。舉個(gè)例子:
ThinModel:
@property (nonatomic, copy) NSString *calId; //日程id
@property (nonatomic, copy) NSString *title; //日程標(biāo)題
ThinHelper:
/*
* 判斷自己是否是該日程參與者
*/
+ (BOOL)isCalendarActory:(NSString *)doneUsers;
Controller:
BOOL ret = [ThinHelper isCalendarActory:@“”];
其優(yōu)點(diǎn)是:
由于ThinModel跟業(yè)務(wù)完全無(wú)關(guān),它的數(shù)據(jù)可以交給任何一個(gè)能處理它數(shù)據(jù)的Helper或其他的對(duì)象,來(lái)完成業(yè)務(wù)。在代碼遷移的時(shí)候獨(dú)立性很強(qiáng),很少會(huì)出現(xiàn)拔出蘿卜帶出泥的情況。另外,由于ThinModel只是數(shù)據(jù)表達(dá),對(duì)它進(jìn)行維護(hù)基本上是0成本,軟件膨脹得再厲害,ThinModel也不會(huì)大到哪兒去。
其缺點(diǎn)是:
缺點(diǎn)就在于,Helper這種做法也不見(jiàn)得很好,這里有一篇文章批判了這個(gè)事情。另外,由于Model的操作會(huì)出現(xiàn)在各種地方,ThinModel在一定程度上違背了DRY(Don't Repeat Yourself)的思路,Controller仍然不可避免在一定程度上出現(xiàn)代碼膨脹。
二丶MVC
M應(yīng)該做的事:
- 給ViewController提供數(shù)據(jù)
- 給ViewController存儲(chǔ)數(shù)據(jù)提供接口
- 提供經(jīng)過(guò)抽象的業(yè)務(wù)基本組件,供Controller調(diào)度
C應(yīng)該做的事:
- 管理View Container的生命周期
- 負(fù)責(zé)生成所有的View實(shí)例,并放入View Container
- 監(jiān)聽(tīng)來(lái)自View與業(yè)務(wù)有關(guān)的事件,通過(guò)與Model的合作,來(lái)完成對(duì)應(yīng)事件的業(yè)務(wù)。
V應(yīng)該做的事:
- 響應(yīng)與業(yè)務(wù)無(wú)關(guān)的事件,并因此引發(fā)動(dòng)畫(huà)效果,點(diǎn)擊反饋(如果合適的話,盡量還是放在View去做)等。
- 界面元素表達(dá)
三丶MVCS
蘋(píng)果自身就采用的是這種架構(gòu)思路,從名字也能看出,也是基于MVC衍生出來(lái)的一套架構(gòu)。從概念上來(lái)說(shuō),它拆分的部分是Model部分,拆出來(lái)一個(gè)Store。這個(gè)Store專(zhuān)門(mén)負(fù)責(zé)數(shù)據(jù)存取。但從實(shí)際操作的角度上講,它拆開(kāi)的是Controller。
這算是瘦Model的一種方案,瘦Model只是專(zhuān)門(mén)用于表達(dá)數(shù)據(jù),然后存儲(chǔ)、數(shù)據(jù)處理都交給外面的來(lái)做。MVCS使用的前提是,它假設(shè)了你是瘦Model,同時(shí)數(shù)據(jù)的存儲(chǔ)和處理都在Controller去做。所以對(duì)應(yīng)到MVCS,它在一開(kāi)始就是拆分的Controller。因?yàn)镃ontroller做了數(shù)據(jù)存儲(chǔ)的事情,就會(huì)變得非常龐大,那么就把Controller專(zhuān)門(mén)負(fù)責(zé)存取數(shù)據(jù)的那部分抽離出來(lái),交給另一個(gè)對(duì)象去做,這個(gè)對(duì)象就是Store。這么調(diào)整之后,整個(gè)結(jié)構(gòu)也就變成了真正意義上的MVCS。
TCStore:
//主要為本地?cái)?shù)據(jù)查詢(xún),刪除,跟新 提供接口
- (NSMutableArray *)fetchLocalFavoritesWithType:(NSString *)type
Controller:
self.dataSource = [[TCStore sharedInstance] fetchLocalFavoritesWithType:@“”];
四丶MVVM
1.純屬打醬油
MVVM去年在業(yè)界討論得非常多,無(wú)論國(guó)內(nèi)還是國(guó)外都討論得非常熱烈,尤其是在ReactiveCocoa這個(gè)庫(kù)成熟之后,ViewModel和View的信號(hào)機(jī)制在iOS下終于有了一個(gè)相對(duì)優(yōu)雅的實(shí)現(xiàn)。MVVM本質(zhì)上也是從MVC中派生出來(lái)的思想,MVVM著重想要解決的問(wèn)題是盡可能地減少Controller的任務(wù)。
2.看著好看有分割線
不管MVVM也好,MVCS也好,他們的共識(shí)都是Controller會(huì)隨著軟件的成長(zhǎng),變很大很難維護(hù)很難測(cè)試。只不過(guò)兩種架構(gòu)思路的前提不同,MVCS是認(rèn)為Controller做了一部分Model的事情,要把它拆出來(lái)變成Store,MVVM是認(rèn)為Controller做了太多數(shù)據(jù)加工的事情,所以MVVM把數(shù)據(jù)加工的任務(wù)從Controller中解放了出來(lái),使得Controller只需要專(zhuān)注于數(shù)據(jù)調(diào)配的工作,ViewModel則去負(fù)責(zé)數(shù)據(jù)加工并通過(guò)通知機(jī)制讓View響應(yīng)ViewModel的改變。
3.臥槽怎么還有
MVVM是基于胖Model的架構(gòu)思路建立的,然后在胖Model中拆出兩部分:Model和ViewModel。關(guān)于這個(gè)觀點(diǎn)我要做一個(gè)額外解釋?zhuān)号諱odel做的事情是先為Controller減負(fù),然后由于Model變胖,再在此基礎(chǔ)上拆出ViewModel,跟業(yè)界普遍認(rèn)知的MVVM本質(zhì)上是為Controller減負(fù)這個(gè)說(shuō)法并不矛盾,因?yàn)榕諱odel做的事情也是為Controller減負(fù)。
另外,我前面說(shuō)MVVM把數(shù)據(jù)加工的任務(wù)從Controller中解放出來(lái),跟MVVM拆分的是胖Model也不矛盾。要做到解放Controller,首先你得有個(gè)胖Model,然后再把這個(gè)胖Model拆成Model和ViewModel。
4.馬上完了
前面扯了那么多,其實(shí)歸根結(jié)底就是一句話:在MVC的基礎(chǔ)上,把C拆出一個(gè)ViewModel專(zhuān)門(mén)負(fù)責(zé)數(shù)據(jù)處理的事情,就是MVVM。然后,為了讓View和ViewModel之間能夠有比較松散的綁定關(guān)系,于是我們使用ReactiveCocoa,因?yàn)樘O(píng)果本身并沒(méi)有提供一個(gè)比較適合這種情況的綁定方法。iOS領(lǐng)域里KVO,Notification,block,delegate和target-action都可以用來(lái)做數(shù)據(jù)通信,從而來(lái)實(shí)現(xiàn)綁定,但都不如ReactiveCocoa提供的RACSignal來(lái)的優(yōu)雅,如果不用ReactiveCocoa,綁定關(guān)系可能就做不到那么松散那么好,但并不影響它還是MVVM。
在實(shí)際iOS應(yīng)用架構(gòu)中,MVVM應(yīng)該出現(xiàn)在了大部分創(chuàng)業(yè)公司或者老牌公司新App的iOS應(yīng)用架構(gòu)圖中,據(jù)我所知易寶支付旗下的某個(gè)iOS應(yīng)用就整體采用了MVVM架構(gòu),他們抽出了一個(gè)Action層來(lái)裝各種ViewModel,也是屬于相對(duì)合理的結(jié)構(gòu)。
所以Controller在MVVM中,一方面負(fù)責(zé)View和ViewModel之間的綁定,另一方面也負(fù)責(zé)常規(guī)的UI邏輯處理。
viewCell
@property (nonatomic, strong) viewModel *model;
Model
@property (nonatomic, copy) NSString *title; //標(biāo)題
viewModel
@property (nonatomic, strong) Model *model;
/**
* 頭像frame
*/
@property (nonatomic, assign, readonly) CGRect headerFrame;
+(void)caluCellHightWith:(RHFlowCategoryCellType)type;
Controller
viewCell.model = self.dataSource[index.row];
五丶VIPER
VIPER(View,Interactor,Presenter,Entity,Routing)。VIPER我并沒(méi)有實(shí)際使用過(guò),我是在objc.io上第13期看到的。
但凡出現(xiàn)一個(gè)新架構(gòu)或者我之前并不熟悉的新架構(gòu),有一點(diǎn)我能夠非常肯定,這貨一定又是把MVC的哪個(gè)部分給拆開(kāi)了(壞笑)。事實(shí)情況是VIPER確實(shí)拆了很多很多,除了View沒(méi)拆,其它的都拆了。
這個(gè)我就不講了,不怎么用,如果有想了解的,請(qǐng)去網(wǎng)站了解.
參考資料:
http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html
(objc.io)
https://www.objc.io/issues/13-architecture/viper/