每一個開發心中都有一個架構的夢,雖然不能像大佬們同樣直接直接給出系統級的架構,可是咱們在平常的編碼過程當中,也能夠慢慢積累一些本身的架構的看法,慢慢提升~ios
由於在學校本身一我的在寫整個App,加之需求也不明確,時常需求變動(在學校的組織寫項目的通病了),因此編寫過程真的是越寫越糟心,因此,不得已對已經開發的一小部分作了重構,如下是本小白在重構過程當中總結的一些看法(不得不說,本科階段講的那些設計模式什麼的,是真的頗有用,只是當時根本理解不了這些精髓,等到重構時才發現均可以套原型)。git
主要就是:自頂向下設計,自底向上實現,先量化數據再優化github
敏捷原則:對擴展開放-對修改封閉sql
1,當一個需求須要多業務合做開發時,若是直接依賴,會致使某些依賴層上端的業務工程師在前期空轉,依賴層下端的工程師任務繁重,致使延期(就會擠壓QA老鐵的時間,而後再找PM撕*。。。。。別問我是怎麼知道的) 2,當要開闢一個新業務時,若是已有各業務間直接依賴,新業務又依賴某個舊業務,就致使新業務的開發環境搭建困難,由於必需要把全部相關業務都塞入開發環境,新業務才能進行開發。 3,當某一個被其餘業務依賴的頁面有所修改時,好比更名,涉及到的修改面就會特別大。影響的是形成任務量和維護成本都上升的結果。數據庫
對應解決方法:依賴下沉,假如A、B、C三個模塊存在橫向依賴,這樣的話引入新節點D,對A、B、C實現依賴下沉,當A調用B的某個頁面的時候,將請求交給Mediater,而後由Mediater經過某種手段獲取到B業務頁面的實例,交還給A就好了。設計模式
關於不跨層訪問說下:api
跨層訪問是指數據流向了跟本身沒有對接關係的模塊。有的時候跨層訪問是不可避免的,好比網絡底層裏面信號從2G變成了3G變成了4G,這是有可能須要跨層通知到View的。但這種狀況很少,一旦出現就要想盡一切辦法在本層搞定或者交給上層或者下層搞定,儘可能不要出現跨層的狀況。跨層訪問一樣也會增長耦合度,當某一層須要總體替換的時候,牽涉面就會很大。緩存
易測試性:安全
儘量減小依賴關係,便於mock。另外,若是是高度模塊化的架構,拓展起來將會是一件很是容易的事情。bash
常常有‘三層架構MVC’這樣的說法,以致於不少人就會認爲三層架構就是MVC,MVC就是三層架構。其實不是的。三層架構裏面其實沒有Controller的概念,並且三層架構描述的側重點是模塊之間的邏輯關係。MVC有Controller的概念,它描述的側重點在於數據流動方向。
全部的模塊角色只會有三種:
View層的架構一旦實現或定型,在App發版後可修改的餘地就已經很是之小了。由於它跟業務關聯最爲緊密,作決策時要拿捏好尺度。
由於View層架構是最貼近業務的底層架構
view層架構知識點主要包括:
#pragma mark - life cycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:self.label];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.label.frame = CGRectMake(1, 2, 3, 4);
}
#pragma mark - getters and setters
- (UILabel *)label
{
if (_label == nil) {
_label = [[UILabel alloc] init];
_label.text = @"1234";
_label.font = [UIFont systemFontOfSize:12];
... ...
}
return _label;
}
@end
複製代碼
不是delegate方法的,不是event response(相應用戶操做)方法的,不是life cycle(view didload這些方法)方法的,就是private method了,這些private methods通常是用於日期換算、圖片裁剪啥的這種輔助的小功能。這些小功能通常都是單獨抽出來寫成模塊的tool類或者系統Util類。
無外乎就是 storyboard+xib+代碼擼的組合
借鑑一下@唐巧的分析腳本: 傳送門: https://gist.github.com/tangqiaoboy/b149d03cfd0cd0c2f7a1 可見這個原本就是有爭議的。
其實,實現簡單的東西,用Code同樣簡單,實現複雜的東西,Code比StoryBoard更簡單。
因此本渣通常採用: 1,複雜頁面主體手擼代碼(用的是masonry) 2,簡單、靜態的Cell以及封裝的一些自定義小控件使用xib。
A.是否須要讓業務方統一派生ViewController。 B.
#MVC MVC架構基礎請看象印筆記。
各個模塊須要負責的事物:
可能還有一些別的設計模式,可是本人能力有限啊啊啊,因此只先介紹這倆
胖Model:(MVVM的基本思想) 包含了部分弱業務邏輯。胖Model要達到的目的是,Controller從胖Model這裏拿到數據以後,不用額外作操做或者只要作很是少的操做,就可以將數據直接應用在View上。
瘦Model:(MVCS的基本思想) 瘦Model只負責業務數據的表達,全部業務不管強弱一概扔到Controller。瘦Model要達到的目的是,盡一切可能去編寫細粒度Model,而後配套各類helper類或方法來對弱業務作抽象,強業務依舊交給Controller。
前言:首先無論MVVM也好,MVCS也好,他們的共識都是Controller會隨着軟件的成長,變很大很難維護很難測試。只不過兩種架構思路的前提不一樣,MVCS是認爲Controller作了一部分Model的事情,要把它拆出來變成Store,MVVM是認爲Controller作了太多數據加工的事情,因此MVVM把數據加工的任務從Controller中解放了出來,使得Controller只須要專一於數據調配的工做,ViewModel則去負責數據加工並經過通知機制讓View響應ViewModel的改變。
從概念上來講,它拆分的部分是Model部分,拆出來一個Store。這個Store專門負責數據存取。但從實際操做的角度上講,它拆開的是Controller。
MVCS使用的前提是,它假設了你是瘦Model,同時數據的存儲和處理都在Controller去作。因此對應到MVCS,它在一開始就是拆分的Controller。由於Controller作了數據存儲的事情,就會變得很是龐大,那麼就把Controller專門負責存取數據的那部分抽離出來,交給另外一個對象去作,這個對象就是Store。這麼調整以後,整個結構也就變成了真正意義上的MVCS。
ReactiveCocoa成熟以後,ViewModel和View的信號機制在iOS下終於有了一個相對優雅的實現(MVC中View和Model是不能直接通訊的,須要Controller作一個協調者的身份)。MVVM本質上也是從MVC中派生出來的思想,MVVM着重想要解決的問題是儘量地減小Controller的任務。
MVVM是基於胖Model的架構思路創建的,而後在胖Model中拆出兩部分:Model和ViewModel。關於這個觀點我要作一個額外解釋:胖Model作的事情是先爲Controller減負,而後因爲Model變胖,再在此基礎上拆出ViewModel,跟業界廣泛認知的MVVM本質上是爲Controller減負這個說法並不矛盾,由於胖Model作的事情也是爲Controller減負。
另外,MVVM把數據加工的任務從Controller中解放出來,跟MVVM拆分的是胖Model也不矛盾。要作到解放Controller,首先你得有個胖Model,而後再把這個胖Model拆成Model和ViewModel。
在MVVM中,Controller扮演的角色:
MVVM的名稱裏沒有C形成了MVVM不須要Controller的錯覺,其實MVVM是必定須要Controller的參與的,雖然MVVM在必定程度上弱化了Controller的存在感,而且給Controller作了減負瘦身(這也是MVVM的主要目的)。其實MVVM應該是Model-ViewModel-Controller-View這樣的架構,並非不須要Controller。
Controller夾在View和ViewModel之間作事情: 1,最主要事情就是將View和ViewModel進行綁定。在邏輯上,Controller知道應當展現哪一個View,Controller也知道應當使用哪一個ViewModel,然而View和ViewModel它們之間是互相不知道的,因此Controller就負責控制他們的綁定關係。 2,常規的UI邏輯處理
一句話總結:
在MVC的基礎上,把Controller拆出一個ViewModel專門負責數據處理的事情,就是MVVM。
再深層次的我就不能很好解釋了:若是須要了解,能夠細看: https://www.teehanlax.com/blog/model-view-viewmodel-for-ios/
MVC實際上是很是高Level的抽象,意思也就是,在MVC體系下還能夠再衍生無數的架構方式,但萬變不離其宗的是,它必定符合MVC的規範。 因此個人建議是:
首先先說下跨層訪問:
當存在A<-B<-C這樣的結構時。當C有事件,經過某種方式告知B,而後B執行相應的邏輯。一旦告知方式不合理,讓A有了跨層知道C的事件的可能,你 就很難保證A層業務工程師在未來不會對這個細節做處理。一旦業務工程師在A層產生處理操做,有多是補充邏輯,也有多是執行業務,那麼這個細節的相關處理代碼就會有一部分散落在A層。然而前者是不該該散落在A層的,後者有多是需求。另外,由於B層是對A層抽象的,執行補充邏輯的時候,有可能和B層針對這個事件的處理邏輯產生衝突,這是咱們很不但願看到的。 但有時跨層數據流通也是不可避免的: 好比,信號從2G變成3G變成4G變成Wi-Fi,這個就是須要跨層數據交流的。
再考慮下文:
大多數App在網絡層所採用的方案主要集中於這三種:Delegate,Notification,Block。 通常都是組合使用,這裏我只能說下我的的選擇,畢竟我的涉獵有限,因此只能結合自身所採用的模式說下好處: 以前在豬場某部門實習,網絡層採用的是block爲主進行數據交付。
當回調以後要作的任務在每次回調時都是一致的狀況下,選擇delegate,在回調以後要作的任務在每次回調時沒法保證一致,選擇block。
//請求發起採用AFN的Block,回調使用delegate方式,這樣在業務方這邊回調函數就可以比較統一,便於維護。
[AFNetworkingAPI callApiWithParam:self.param successed:^(Response *response){
if ([self.delegate respondsToSelector:@selector(successWithResponse:)]) {
[self.delegate successedWithResponse:response];
}
} failed:^(Request *request, NSError *error){
if ([self.delegate respondsToSelector:@selector(failedWithResponse:)]) {
[self failedWithRequest:request error:error];
}
}];
複製代碼
緣由:使用Delegate可以很好地避免跨層訪問,同時限制了響應代碼的形式,相比Notification而言有更好的可維護性,而Notification則解決了跨層數據流通的相應需求。
可是使用Notification必定要約定好命名規範,否則會引起後期維護的災難。
集約型API調用其實就是全部API的調用只有一個類,而後這個類接收API名字,API參數,以及回調着陸點(block,或者delegate等各類模式的着陸點)做爲參數。而後執行相似startRequest這樣的方法,它就會去根據這些參數起飛去調用API了,而後得到API數據以後再根據指定的着陸點去着陸。
離散型API調用是這樣的,一個API對應於一個APIManager,而後這個APIManager只須要提供參數就能起飛,API名字、着陸方式都已經集成入APIManager中。
理想狀況是但願API的數據下發以後就可以不須要進一步處理直接被View所展現。首先要說的是,這種狀況很是少。另外,這種作法使得View和API聯繫緊密,也是不該該發生的。 舉個栗子:
先定義一個protocol:
@protocol AdapatorProtocol <NSObject>
- (NSDictionary)reformDataWithManager:(APIManager *)manager;
@end
在Controller裏是這樣:
@property (nonatomic, strong) id< AdapatorProtocol > XAdapator;
#pragma mark - APIManagerDelegate
- (void)apiManagerDidSuccess:(APIManager *)manager
{
NSDictionary *XData = [manager fetchDataWithReformer:self. XAdapator];
[self.XView configWithData:XData];
}
在APIManager裏面,fetchDataWithReformer是這樣:
- (NSDictionary)fetchDataWithReformer:(id< AdapatorProtocol >)adapator{
if (adapator == nil) {
return self.rawData;
} else {
//adapaor進行處理數據
return [adapator reformDataWithManager:self];
}
}
複製代碼
1,使用緩存(本地混存+URL緩存)進行請求次數的減小,能不發請求的就儘可能不發請求,必需要發請求時,能合併請求的就儘可能合併請求。 2,須要上傳的日誌,積滿必定數量再上傳 3,通常項目都有多個服務器,應用啓動的時候得到本地列表中全部IP的ping值,而後將Dev_URL中的HOST修改成咱們找到的最快的IP。另外,這個本地IP列表也會須要經過一個API來維護,通常是天天第一次啓動的時候ping一下,而後更新到本地。 4,比較大的數據壓縮再上傳。
首先有很是多的方案可供選擇: 一、NSUserDefault: 通常是小規模數據,弱業務相關數據,NSUserDefault變大會影響App啓動的時間,敏感數據不要放NSUserDefault,雖然NSUserDefault的存取真的是很方便。 二、KeyChain:Keychain是蘋果提供的帶有可逆加密的存儲機制,廣泛用在各類存密碼的需求上。另外,因爲App卸載只要系統不重裝,Keychain中的數據依舊可以獲得保留,以及可被iCloud同步的特性,你們都會在這裏存儲用戶惟一標識串。因此有須要加密、須要存iCloud的敏感小數據,通常都會放在Keychain。 三、File:主要包括:
1,Plist 2,archive(歸檔):只適合存一些不常用的,大量的數據,讀取以後直接換變爲對象/直接將對象存儲(須要支持) 3,Stream(直接存文件):適合數據較大且常用,可是文件通常都是遍歷才能拿到,因此建議爲文件創建數據庫索引
四、基於數據庫的無數子方案(YYCache,FMDB,sqlite,CoreData[本人不喜歡用])。數據庫中的數據應該都是強業務相關的,而且不能是很大的文件,好比一個大圖片或者視頻之類的,通常是存文件,而後數據中存放文件路徑這樣配合,而推薦直接使用YYCache,一站式服務。
所以,當有須要持久化的需求的時候,咱們首先考慮的是應該採用什麼手段去進行持久化。
數據庫記得作線程處理(原理跟iOS的屬性的線程安全同樣) 好比SQLite庫就推薦使用Serialized(默認):串行隊列訪問,雖然會慢一丟丟,可是方便易維護。
持久層有專門負責對接View層模塊或業務的DataCenter,它們之間經過Record來進行交互。DataCenter向上層提供業務友好的接口,這通常都是強業務:好比根據用戶篩選條件返回符合要求的數據等。而後DataCenter在這個接口裏面調度各個Table,作一系列的業務邏輯,最終生成record對象,交付給View層業務。
DataCenter爲了要完成View層交付的任務,會涉及數據組裝和跨表的數據操做。數據組裝由於View層要求的不一樣而不一樣,所以是強業務。跨表數據操做本質上就是各單表數據操做的組合,DataCenter負責調度這些單表數據操做從而得到想要的基礎數據用於組裝。那麼,這時候單表的數據操做就屬於弱業務,這些弱業務就由Table映射對象來完成。 Table對象經過QueryCommand來生成相應的SQL語句,並交付給數據庫引擎去查詢得到數據,而後交付給DataCenter。
差很少就這些,等重構完再補充~