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