iOS架構(gòu)補(bǔ)完計(jì)劃--淺談MVC及其衍生架構(gòu)模式(附簡(jiǎn)易圖解)

目錄

  • 概述
  • 傻瓜圖解

  • MVC
    • 一個(gè)正統(tǒng)的MVC、三者的任務(wù)是什么?
    • 關(guān)于View到底該不該寫(xiě)一些業(yè)務(wù)代碼
  • 胖Model與瘦Model
    • 強(qiáng)業(yè)務(wù)谚鄙、弱業(yè)務(wù)
    • 胖Model
    • 瘦Model
    • 該用哪個(gè)各拷?
  • MVVM
    • Model
    • View
    • ViewModel
    • Controller
    • ReactiveCocoa對(duì)于MVVM的意義是什么?
  • MVCS
  • MVP
  • VIPER
  • 關(guān)于架構(gòu)設(shè)計(jì)闷营、一些觀點(diǎn)
    • 控制好Controller的代碼量
    • 對(duì)于MVX如何選擇
    • 無(wú)論用哪種模式烤黍、都要深刻的理解每個(gè)模塊不同的職責(zé)

概述

其實(shí)只要是架構(gòu)上的設(shè)計(jì)、本質(zhì)上都是三個(gè)角色:數(shù)據(jù)管理者傻盟、數(shù)據(jù)加工者速蕊、數(shù)據(jù)展示者
不管是MVC娘赴、MVVM规哲、MVP、VIPER或者任何新的設(shè)計(jì)模式诽表、都跳不出這三個(gè)角色唉锌。無(wú)非是把數(shù)據(jù)管理者的工作進(jìn)行拆分隅肥、唯一的界定標(biāo)準(zhǔn)就是把工作拆分的粒度大小。
而無(wú)論哪種思想袄简、最終都逃不開(kāi)三個(gè)問(wèn)題的取舍腥放。代碼量通用性绿语、可讀性秃症。

這里我主要寫(xiě)的是MVC和MVVM、對(duì)于其他的架構(gòu)只是略微提及吕粹。

傻瓜圖解

這兩天總有人跟我說(shuō)我想看Demo...我想了想覺(jué)得Demo其實(shí)也不太直觀伍纫、干脆畫(huà)幾個(gè)圖好了。
主要是想表達(dá)每個(gè)模塊里應(yīng)該放什么類型的東西昂芜、線可能有連得不對(duì)的地兒。親們盡量意會(huì)吧赔蒲。

  • MVC

  • MVC--胖Model

  • MVC--瘦Model

  • MVC--胖瘦結(jié)合

  • MVVM

  • MVP

  • VIPER

試了試...感覺(jué)畫(huà)著費(fèi)勁泌神。能理解了前面幾個(gè)的話、看文字應(yīng)該也能理解VIPER吧舞虱。


MVC

MVC就是典型的著重通用型與可讀性欢际、這正是一個(gè)作為萬(wàn)物之初的架構(gòu)所需要保證的事。簡(jiǎn)單矾兜、易學(xué)损趋。

  • Model進(jìn)行數(shù)據(jù)管理
  • View進(jìn)行數(shù)據(jù)展示
  • Controller負(fù)責(zé)根據(jù)需求對(duì)Model以及View進(jìn)行調(diào)配。

不過(guò)和廣義的MVC不同椅寺、客戶端(別的我不知道啊浑槽、起碼iOS)由于UIViewController自帶一個(gè)容器View、所以除了上述的正統(tǒng)任務(wù)之外返帕、Controller還需要承擔(dān)View的生成桐玻、布局等的任務(wù)。

一個(gè)正統(tǒng)的MVC荆萤、三者的任務(wù)是什么?

所以镊靴、我們可以將MVC三者的任務(wù)再進(jìn)一步細(xì)化一下

  • Model:
    • Controller的讀取提供數(shù)據(jù)
    • Controller的寫(xiě)入提供接口
    • Controller提供基本的業(yè)務(wù)組件

最常用的就是網(wǎng)絡(luò)請(qǐng)求之后Json轉(zhuǎn)Model、寫(xiě)入數(shù)據(jù)庫(kù)之前Model轉(zhuǎn)Json链韭。

  • View:
    • 界面的展示
    • 響應(yīng)與業(yè)務(wù)無(wú)關(guān)的事件(動(dòng)畫(huà)效果偏竟、點(diǎn)擊反饋、點(diǎn)擊事件的開(kāi)關(guān)保護(hù)等等)

何為業(yè)務(wù)事件:Model數(shù)據(jù)的改變敞峭、網(wǎng)絡(luò)請(qǐng)求的發(fā)送踊谋、頁(yè)面的跳轉(zhuǎn)、頁(yè)面的刷新等等旋讹。

  • Controller
    • 管理self.view的生命周期
    • 負(fù)責(zé)生成所有的View實(shí)例褪子、以及布局量淌。
    • 將恰當(dāng)?shù)?code>Model交付給View展示。
    • 監(jiān)聽(tīng)來(lái)自View與業(yè)務(wù)有關(guān)的事件嫌褪、通過(guò)與Model的合作呀枢、來(lái)完成對(duì)應(yīng)事件的業(yè)務(wù)。
  • 關(guān)于View到底該不該寫(xiě)一些業(yè)務(wù)代碼

其實(shí)自己以前笼痛。也會(huì)圖方便裙秋、把一些自認(rèn)為的弱業(yè)務(wù)寫(xiě)在View里。

舉個(gè)例子:
  • 一個(gè)Cell缨伊、有用戶Nickname摘刑、還有一個(gè)用戶頭像的Button。

點(diǎn)擊事件肯定由Cell捕獲刻坊。這個(gè)時(shí)候跳轉(zhuǎn)用戶主頁(yè)的動(dòng)作枷恕、該由View完成、還是傳遞給Controller谭胚?
假設(shè)我們交由View徐块、也就是當(dāng)前的Cell跳轉(zhuǎn)了。很方便灾而、省去了寫(xiě)代理的小十行代碼胡控。
并且這個(gè)Cell也可以挪到其他頁(yè)面去使用、一樣能跳到用戶主頁(yè)旁趟、又省去不少代碼昼激。

  • 有一天、產(chǎn)品讓你在某個(gè)頁(yè)面點(diǎn)擊頭像不執(zhí)行任何動(dòng)作锡搜。

咋辦呢橙困?機(jī)智如你、給這個(gè)Cell添加了一個(gè)bool值來(lái)控制是否跳轉(zhuǎn)就好了耕餐。

  • 又過(guò)了幾天纷宇、產(chǎn)品讓你在某個(gè)頁(yè)面把這個(gè)頭像弄成點(diǎn)擊之后彈出舉報(bào)框。

這怎么搞呢蛾方?也不是不行像捶、你又給這個(gè)Cell添了一個(gè)枚舉的type。這簡(jiǎn)直完美桩砰、不同的Type執(zhí)行不同的事件拓春、你順便取消了那個(gè)bool、把他也寫(xiě)成了一個(gè)type亚隅。

  • 又過(guò)了一陣子硼莽、產(chǎn)品又告訴你當(dāng)滿足某些條件的時(shí)候、這個(gè)頭像跳主頁(yè)。另一些條件的時(shí)候懂鸵、這個(gè)頭像不能跳偏螺。

于是、你終于寫(xiě)了個(gè)block或者代理匆光、在點(diǎn)擊之后執(zhí)行一下再看下一步怎么跳轉(zhuǎn)套像。
此時(shí)、回過(guò)頭來(lái)再看你的Cell终息、已經(jīng)面目全非了夺巩、充斥著各種業(yè)務(wù)判斷。

可能你會(huì)說(shuō)周崭、如果產(chǎn)品真的這么二逼柳譬。那我干脆再copy一個(gè)Cell就好了啊。

但是別忘了续镇、你當(dāng)初這么設(shè)計(jì)這個(gè)Cell的時(shí)候可是為了節(jié)省下頁(yè)面跳轉(zhuǎn)的小十行代碼美澳、而你現(xiàn)在卻要為此付出copy一整個(gè)Cell的代價(jià)。

其實(shí)還有一個(gè)更重要的問(wèn)題摸航、就是你這個(gè)View的模塊復(fù)用基本為0

假設(shè)你需要另起一個(gè)新的工程寫(xiě)一個(gè)demo制跟、如果用這個(gè)View你首先要解決一大堆跳轉(zhuǎn)代碼上Controller 文件的缺失、然后還會(huì)發(fā)現(xiàn)忙厌、原來(lái)寫(xiě)的很多邏輯、type在這個(gè)demo里毫無(wú)用處江咳。挨個(gè)刪除逢净、梳理邏輯又要耗費(fèi)很多時(shí)間。

而這些將來(lái)會(huì)發(fā)生的問(wèn)題歼指、如果你最開(kāi)始不把業(yè)務(wù)事件代碼硬寫(xiě)進(jìn)View里爹土、一件都不會(huì)發(fā)生。

胖Model與瘦Model

這里先要引出兩個(gè)概念踩身。強(qiáng)業(yè)務(wù)胀茵、弱業(yè)務(wù)
二者關(guān)鍵的區(qū)別是代碼變動(dòng)的頻率大小與涉及模塊的多少挟阻。舉兩個(gè)例子:
1琼娘、比如把時(shí)間戳轉(zhuǎn)化小數(shù)點(diǎn)的格式化或者修改A屬性進(jìn)行一系列計(jì)算并且改變B屬性附鸽、這種業(yè)務(wù)就屬于弱業(yè)務(wù)脱拼。
2、再比如一個(gè)一個(gè)訂單Model的確認(rèn)收貨坷备、就應(yīng)該歸入沒(méi)辦法歸入弱業(yè)務(wù)熄浓、因?yàn)樯婕熬W(wǎng)絡(luò)請(qǐng)求、加密等等多個(gè)底層模塊省撑。

  • 胖Model

主旨是Controller從Model里拿到的數(shù)據(jù)赌蔑、不需要進(jìn)行更多的判斷俯在、處理等操作、就能使用娃惯。舉個(gè)例子:

Raw Data:
    timestamp:1234567

FatModel:
    @property (nonatomic, assign) CGFloat timestamp;
    - (NSString *)ymdDateString; // 2015-04-20 15:16
    - (NSString *)gapString; // 3分鐘前跷乐、1小時(shí)前、一天前石景、2015-3-13 12:34

Controller:
    self.dateLabel.text = [FatModel ymdDateString];
    self.gapLabel.text = [FatModel gapString];

這就需要將弱業(yè)務(wù)劈猿、寫(xiě)進(jìn)Model、很好的滿足的復(fù)用的需求潮孽。
胖Model也是存在問(wèn)題的揪荣、就是移植的困難。畢竟業(yè)務(wù)再弱往史、也是代碼仗颈、當(dāng)項(xiàng)目成長(zhǎng)到一定程度、這個(gè)Model也將會(huì)變得相當(dāng)?shù)挠纺[椎例。

  • 瘦Model

就是要把MVC的M貫徹倒底挨决、除了業(yè)務(wù)的表達(dá)啥都不管。
但是這樣又會(huì)導(dǎo)致Controller中的代碼變得異常臃腫(廢話么订歪、連時(shí)間戳轉(zhuǎn)化都要交給Controller不腫才怪)
所以瘦Model要借助一些外來(lái)的輔助模塊(索性可以叫Helper)來(lái)對(duì)弱業(yè)務(wù)做抽象脖祈。舉個(gè)例子:

Raw Data:
{
    "name":"casa",
    "sex":"male",
}

SlimModel:
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, strong) NSString *sex;

Helper:
    #define Male 1;
    #define Female 0;
    + (BOOL)sexWithString:(NSString *)sex;

Controller:
    if ([Helper sexWithString:SlimModel.sex] == Male) {
        ...
    }
  • 該用哪個(gè)?

我個(gè)人用胖Model用的比較多刷晋、但是也借助了一些瘦Model的思想盖高。舉例來(lái)講:
除了上述很明確的可以放到Model里的弱業(yè)務(wù)之外、像一個(gè)訂單中的確認(rèn)收貨眼虱、發(fā)貨喻奥、申請(qǐng)退貨等等操作、他們既不算特別強(qiáng)的業(yè)務(wù)捏悬、而且還有很高的復(fù)用需求(訂單列表和訂單詳情都需要確認(rèn)收貨)撞蚕。

這種業(yè)務(wù)有一種特點(diǎn)、就是代碼就在哪里过牙。不管你放到哪甥厦、都只能挪不能刪。但挪到哪寇钉、都不完全合適矫渔。從定義上來(lái)講十分莫若兩可。個(gè)人覺(jué)得:
  • 這種的業(yè)務(wù):能不放在Controller里就不要放
    • 你可以干脆放到胖Model里摧莽、畢竟將來(lái)拆分一個(gè)400行的Model庙洼、比拆分一個(gè)1400行的Controller容易得多。
    • 你也可以單獨(dú)新建一個(gè)Helper、配合著Model來(lái)完成業(yè)務(wù)油够。這樣想移植頁(yè)面就單用Model蚁袭、想帶業(yè)務(wù)移植就帶著Helper(其實(shí)這個(gè)思路已經(jīng)很接近MVP了、但是還差提點(diǎn)石咬。MVP還需要為View提供數(shù)據(jù))揩悄。

MVVM

弱弱的一說(shuō)、我并不推薦iOS中寫(xiě)MVVM(因?yàn)槿肭中詫?shí)在太強(qiáng))鬼悠、不會(huì)教你怎么用RAC怎么寫(xiě)出MVVM删性、只是想讓你理解什么是MVVM
MVVM現(xiàn)在已經(jīng)是一種非常成熟的思想了。應(yīng)用也十分普及焕窝、例如Vue以及小程序蹬挺。
MVVM的初衷也是為了Controller減負(fù)。
剛才的胖Model只從Controller移植走了一些簡(jiǎn)單的弱業(yè)務(wù)它掂。
ViewModel則干脆把數(shù)據(jù)的處理全部從Controller移植了出去巴帮。
理想上相同的輸入(比如網(wǎng)絡(luò)服務(wù)響應(yīng))將會(huì)導(dǎo)出相同的輸出(屬性的值)。

簡(jiǎn)單的說(shuō)一下M虐秋、V榕茧、VM的在架構(gòu)中所扮演的角色。

  • Model:
和正統(tǒng)MVC中的瘦Model一樣客给、只承載最基本的數(shù)據(jù)單元用押。
@interface UserListModel: NSObject
  
@property (nonatomic, strong, readonly) NSString *userName;
@property (nonatomic, strong, readonly) UIImage *portraitImg;

@end
  • View
其實(shí)也和正統(tǒng)的MVC一樣、只做展示工作靶剑、不承接任何業(yè)務(wù)邏輯蜻拨。

但是需要注意的是、有時(shí)候也會(huì)在View中將ViewModelView做一些綁定工作(ViewModel本質(zhì)上也算是Model層抬虽、所以View并不適合直接持有ViewModel)官觅。

- (void) awakeFromNib {
    [super awakeFromNib];
    RAC(self. portraitImgView,  image) = RACObserve(self,  viewModel. portraitImg);
    RAC(self. userNameLabel,  text) = RACObserve(self,  viewModel. userName);
}
  • ViewModel
提供了這個(gè)頁(yè)面展示所有需要的數(shù)據(jù)的一個(gè)對(duì)象纵菌。

舉一個(gè)簡(jiǎn)單的例子:


@interface UserListViewModel: NSObject
@property (nonatomic, assign, readonly) BOOL loading;
@property (nonatomic, strong, readonly) NSArray <UserListModel *>*userList;
@property (nonatomic, strong, readwrite) NSString *searchUserName;
  
- (void) searchUser;
- (void) deleteUserWithModel:(UserListModel *)model;
- (void) loadMoreUser;

這個(gè)ViewModel里涵蓋了所有頁(yè)面展示需要的要素阐污。用戶列表、搜索名稱咱圆、是否需要顯示網(wǎng)絡(luò)加載的小菊花笛辟。
并且涵蓋了對(duì)這些數(shù)據(jù)的所有操作方法。加載更多序苏、搜索手幢、刪除。
但是忱详、ViewModel到底也是一個(gè)Model層围来、不應(yīng)該引入U(xiǎn)IKit(View層)。如果刪除需要彈窗、那么這個(gè)彈窗動(dòng)作是不應(yīng)該交給ViewModel來(lái)搞的监透、因?yàn)檫@已經(jīng)不屬于數(shù)據(jù)處理的范疇了
仔細(xì)想想桶错、這個(gè)ViewModel其實(shí)就是把Controller中與頁(yè)面相關(guān)的數(shù)據(jù)處理代碼挪進(jìn)來(lái)了而已。
如此胀蛮、我們?cè)O(shè)置可以脫離View層院刁。拿著這個(gè)ViewModel去跑單元測(cè)試。簡(jiǎn)直碉堡粪狼。

  • Controller
    雖然MVVM中沒(méi)有體現(xiàn)出C的字眼退腥、但是實(shí)際操作肯定是要遵循著View <-> C <-> ViewModel <-> Model
    起碼在iOS中是再榄、這和Vue中簡(jiǎn)單粗暴的方式不同:
//頁(yè)面里
<p>{{message}}</p>
<li v-for="value in arr">
       {{value}}
</li>

//js文件里
new Vue({
//數(shù)據(jù)
       data:{
             key:'welcome vue',
             arr:['apple','banana','orange','pear'],
             json:{a:'apple',b:'banana',c:'orange'}
       }
      //方法
      methods:{
            add:function(){
            //push 添加元素
                  this.arr.push('tomato');
            }
      }
})

因?yàn)镠tml中并沒(méi)有明確的Controller的概念狡刘、整個(gè)Html文件就是Controller容器。
和iOS的區(qū)別很明顯:
除去精煉的寫(xiě)法不跟、整個(gè)Html文件所關(guān)聯(lián)的js資源都可以無(wú)障礙互通颓帝、所以View層無(wú)時(shí)無(wú)刻不持有著Model層、在View層直接綁定更方便窝革。

  • 所以iOS中Controller的作用就顯而易見(jiàn)了
    Controller夾在ViewViewModel之間做的其中一個(gè)主要事情就是將ViewViewModel進(jìn)行綁定购城。在邏輯上、Controller知道應(yīng)當(dāng)展示哪個(gè)View虐译、Controller也知道應(yīng)當(dāng)使用哪個(gè)ViewModel瘪板、然而ViewViewModel它們之間是互相不知道的、所以Controller就負(fù)責(zé)控制他們的綁定關(guān)系漆诽。

  • ReactiveCocoa對(duì)于MVVM的意義是什么侮攀?

ReactiveCocoa并不是MVVM思想的根本、不用ReactiveCocoa也能MVVM厢拭、用ReactiveCocoa能更好地體現(xiàn)MVVM的精髓兰英。
我一直強(qiáng)調(diào)MVC中的MV是應(yīng)該盡量不要互相持有的。
這個(gè)時(shí)候如何把原本松散的二者通過(guò)C緊密的聯(lián)系起來(lái)供鸠、就要進(jìn)行數(shù)據(jù)綁定畦贸。
而這種數(shù)據(jù)綁定、iOS本身并沒(méi)有什么太靠譜的辦法(就像剛才前端例子中的<p>{{message}}</p>楞捂、這種寫(xiě)法)薄坏。

雖然KVO、Notification寨闹、block胶坠、delegate和target-action都可以用來(lái)做數(shù)據(jù)通信進(jìn)而實(shí)現(xiàn)綁定

但都不如ReactiveCocoa來(lái)的《《《優(yōu)雅》》》。

對(duì)繁堡、這就是我開(kāi)始說(shuō)為什么RAC對(duì)于MVVM不是必須的沈善。
如果不用ReactiveCocoa乡数、綁定關(guān)系可能就做不到那么松散那么好、但并不影響它還是MVVM闻牡。


MVCS

將數(shù)據(jù)持久化的代碼移植給了store...


MVP

實(shí)際上就是將Controller中關(guān)于ModelView的調(diào)配處理的代碼移植了過(guò)來(lái)瞳脓。各部分分工如下:

  • View
    負(fù)責(zé)界面展示和布局管理、向Presenter暴露視圖更新和數(shù)據(jù)獲取的接口
  • Presenter
    負(fù)責(zé)接收來(lái)自View的事件澈侠、通過(guò)View提供的接口更新視圖劫侧,并管理Model。
  • Model
    和MVC中的一樣哨啃,提供數(shù)據(jù)模型

VIPER

除了View沒(méi)拆烧栋、其它的都拆了....
在MVP的基礎(chǔ)上新增了Interactor與Router

  • View
    • 提供完整的視圖。負(fù)責(zé)視圖的組合拳球、布局审姓、更新
    • 向Presenter提供更新視圖的接口
    • 將View相關(guān)的事件發(fā)送給Presenter
  • Interactor
    • 維護(hù)主要的業(yè)務(wù)邏輯功能,向Presenter提供現(xiàn)有的業(yè)務(wù)用例
    • 維護(hù)祝峻、獲取魔吐、更新Entity
    • 當(dāng)有業(yè)務(wù)相關(guān)的事件發(fā)生時(shí)、處理事件莱找、并通知Presenter
  • Presenter
    • 接收并處理來(lái)自View的事件
    • 向Interactor請(qǐng)求調(diào)用業(yè)務(wù)邏輯
    • 向Interactor提供View中的數(shù)據(jù)
    • 接收并處理來(lái)自Interactor的數(shù)據(jù)回調(diào)事件
    • 通知View進(jìn)行更新操作
    • 通過(guò)Router跳轉(zhuǎn)到其他View
  • Entity
    • 和Model一樣的數(shù)據(jù)模型
  • Router
    • 提供View之間的跳轉(zhuǎn)功能酬姆、減少了模塊間的耦合
    • 初始化VIPER的各個(gè)模塊

VIPER與其他的架構(gòu)相比最大的優(yōu)勢(shì)就是粒度簡(jiǎn)直細(xì)化成了塵埃、極大的提高了可測(cè)性奥溺。
但問(wèn)題也相當(dāng)顯著辞色、層級(jí)越多、數(shù)據(jù)傳遞的工作量(API)就越大浮定。文件越多相满、新建一個(gè)頁(yè)面的成本也就越高。


關(guān)于架構(gòu)設(shè)計(jì)桦卒、一些觀點(diǎn)

除了MVVM饶囚、它對(duì)iOS的入侵性簡(jiǎn)直太高政恍、主要取決于團(tuán)隊(duì)的決策(比如是不是新項(xiàng)目程拭、Leader是不是想玩玩看)讼渊。

  • 控制好Controller的代碼量

隨著項(xiàng)目的進(jìn)行、代碼量最多只能優(yōu)化迎吵、膨脹不可避免躲撰。
而在沒(méi)辦法繼續(xù)精簡(jiǎn)的前提下针贬、想控制Controller的代碼量击费。就要在可讀性和通用性之間進(jìn)行取舍。該挪走的時(shí)候就挪走吧桦他、畢竟梳理一個(gè)單獨(dú)的模塊蔫巩、比梳理一個(gè)幾千行的Controller要方便多了谆棱。

  • 對(duì)于MVX如何選擇
    • 其實(shí)完全要看業(yè)務(wù)的性質(zhì)以及復(fù)雜度。
    • 如果你一個(gè)頁(yè)面只有一個(gè)UITableView圆仔、搞出一些奇淫技巧其實(shí)意義 不大垃瞧、徒增煩惱。踏踏實(shí)實(shí)用MVC對(duì)大家都好坪郭。
    • 如果感覺(jué)業(yè)務(wù)里有非常多的View與Model互通个从、或者需大量復(fù)用、可以用MVP歪沃。
    • 如果有大量的數(shù)據(jù)讀寫(xiě)嗦锐、可以用MVCS。
    • 如果業(yè)務(wù)相當(dāng)?shù)膹?fù)雜沪曙、耦合讓人渾身難受奕污。做好模塊化或者干脆VIPER才是出路。
  • 無(wú)論用哪種模式液走、都要深刻的理解每個(gè)模塊不同的職責(zé)

比如MVVM里的VM碳默、既然是Model層、就不要把UIKit放進(jìn)去缘眶。
再比如MVP中的P嘱根、既然是為了幫助Controller協(xié)調(diào)M與V而生、就不要把與二者無(wú)關(guān)的工作也搶過(guò)來(lái)干巷懈。

參考資料

(要感謝評(píng)論區(qū)SlowMaker的提醒)
寫(xiě)的時(shí)候主要的結(jié)構(gòu)思路借鑒了兩年前入行時(shí)看過(guò)的一篇博客《《iOS應(yīng)用架構(gòu)談 view層的組織和調(diào)用方案》》儿子、對(duì)其中自己認(rèn)為不太好理解的部分進(jìn)行了補(bǔ)充和擴(kuò)展。
也推薦一下大神微博:@反革命攻城獅CasaTaloyum
很推薦去搜藏并且拜讀一下CasaTaloyum的博客砸喻、會(huì)讓你對(duì)架構(gòu)整體的把控有一個(gè)質(zhì)的提升柔逼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市割岛,隨后出現(xiàn)的幾起案子愉适,更是在濱河造成了極大的恐慌,老刑警劉巖癣漆,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件维咸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惠爽,警方通過(guò)查閱死者的電腦和手機(jī)癌蓖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)婚肆,“玉大人租副,你說(shuō)我怎么就攤上這事〗闲裕” “怎么了用僧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵结胀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我责循,道長(zhǎng)糟港,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任院仿,我火速辦了婚禮秸抚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歹垫。我一直安慰自己耸别,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布县钥。 她就那樣靜靜地躺著秀姐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪若贮。 梳的紋絲不亂的頭發(fā)上省有,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音谴麦,去河邊找鬼蠢沿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匾效,可吹牛的內(nèi)容都是我干的舷蟀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼面哼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼野宜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起魔策,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤匈子,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后闯袒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體虎敦,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年政敢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了其徙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喷户,死狀恐怖唾那,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摩骨,我是刑警寧澤通贞,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站恼五,受9級(jí)特大地震影響昌罩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灾馒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一茎用、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧睬罗,春花似錦轨功、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至花盐,卻和暖如春羡滑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背算芯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工柒昏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人熙揍。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓职祷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親届囚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子有梆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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