[iOS] 魔法數字 “3”
作為24k高純度理工技術男,從大量和女性朋友交流的經歷中,我發現一個有意思的現象: 男性在聊天的時候,一般會習慣就某一個話題持續深入的探討,而女性總是在無意識當中頻繁的切換話題 。如果將話題的切換比作線程的context switch,男人的并發執行能力和女人相比,簡直弱爆了。在經歷無數的挫敗之后,我也終于發現了和女性朋友愉快聊天的關鍵法則: 不要嘗試去記住10分鐘之前的聊天內容 。
男人的大腦雖然不太擅長context switch,計算機卻是這方面的行家。現在的手機系統無論Android,還是iOS,同時在100個線程之間進行context switch也不會down機。可惜的是計算機在聊天方面缺乏“情趣”,又或者我們工科男在女神眼里并不比一臺冷冰冰的電腦多少高明多少,女神和Siri聊天可能會更開心點你信不信?
不管我們愿不愿意承認,我們人類的大腦其實“不太好用”。“腦子轉的太慢”,“記性不太好了”,“事情太多,我處理不過來了”等等,都是現實低于預期的表現。雖然存在個別大腦性能爆表的例子,比如28歲就憋出了[相對論]的愛因斯坦“大叔”,絕大部分人的大腦都只能用平庸來形容。我保守一點評價,依據“二八法則”,80%人的大腦瓶頸都是”3“。3是什么鬼?3不是你的智商值絕對值,3是人腦并發的一道坎。
- 和朋友聊天時,如果連續切換3個話題,這場聊天你就會感覺吃力了。
- 如果同一時刻有3件高優先級的事情要處理,你就會感覺有些分身乏術了。
- 如果要和3個朋友同時聊天,你必須高度集中注意力才能應對自如。
我還可以舉出更多的例子,這些例子有一個共同點,都在嘗試挑戰人腦的并發能力。
魔法數字3之編程
自詡為計算機世界魔法師的程序員們也擺脫不了魔法數字3的限制。雖然有少部分程序員天賦異稟,領悟力創造力驚人,寫代碼自成一格,但我們所寫的代碼最終還是要能被大部分人所理解。計算機行業越發達,個人英雄也會越來越少,團隊合作是大趨勢。嘗試去遵循,敬畏人腦的局限性能讓我們寫出更優質的代碼,以下列出一些小tip,皆來自個人這么些年的摸爬滾打,希望能對初入行的朋友們有些幫助。
方法參數的個數<=3
當我們定義一個新方法的時候,參數的個數越少越好,如果業務邏輯復雜,也不要讓參數的個數超過三個。比如下面這個方法定義(以下例子說明都使用Objective-C語法):
- (void)initUserWithName:(NSString*)name gender:(int)gender age:(int)age
{
self.name = name;
self.gender = gender;
self.age = age;
}
上面這段代碼嘗試初始化一個User的信息,包括名字,性別和年齡,簡單好理解。方法的使用者一眼就記住怎么使用。如果我再加一個參數呢:
- (void)initUserWithName:(NSString*)name gender:(int)gender age:(int)age height:(int)height
{
self.name = name;
self.gender = gender;
self.age = age;
self.height = height;
}
多了一個年齡的參數,方法的使用者在初始化User的時候要記得同時傳入4個信息。大家可以嘗試下感受這其中的差別,現實當中我們很有可能要面對比User更復雜的業務model,要傳入更陌生的業務參數。方法調用者記住3個參數的概率要遠高于記住4個,一旦調用者忘記第四個參數是什么,就必須jump到方法的declaration,同時必須暫停當前的思考,進行一次大腦的context switch,切換到方法是怎么定義的理解上去,記住第四個參數之后,再切回之前的工作場景。這其中兩次的switch,看似微乎其微,積少成多,對工作效率和心情的影響不言而喻。自己寫的方法會記憶深刻一些,但程序員職業生涯中大部分時候都是在讀別人的代碼,而且讀自己四五年前寫的代碼,可能和讀別人代碼并沒太大差別,寫方法的時候參數少一點,是對別人負責,也是對自己負責。
當然有些業務會復雜,需要的參數就有5個甚至6個,遇到這種情況,可以嘗試將方法抽象成一個對象,構建對象之后再設置具體的任務屬性,或者使用java當中經常使用的builder pattern來構建。
套嵌的if層級<=3
對于套嵌的if層級,和方法的調用層級類似,不要超過3層。比如下面這段代碼:
if (a >= 0) {
//mess up with a if (b >= 0) {
//mess up with b if (c >= 0) {
//mess up with c }
}
}
有些程序員習慣寫很深的if套嵌,每一個if里面邏輯可能還很長(即使作者當時寫的簡短,總有一天也會被別人寫得又臭又長)。這種套嵌的if讀起來真得很費勁,調試起來也麻煩,經常需要人眼去對齊大括號,來判斷if的包含范圍。如果這幾個if當中再放幾個return,debug的滋味就更酸爽了。
盡量避免if套嵌是個好習慣,當然有些邏輯用套嵌的if表達起來會更順暢一些,這種情況下,請不要讓套嵌的層級超過3個,同時還要避免單個if當中的代碼量盡量簡短。
方法調用層級<=3
我們在調試別人代碼的時候,經常會從某一個方法入手作為debug的起點,從一個方法jump到另一個方法,在查看完所以變量之后,再繼續往更深的函數調用排查問題。每一次的方法調用切換都是一次context switch,對于debug來說,你往往需要同時記住之前所有的context才能作出完整準確的推理,所以一旦方法的調用層級超過3個,大腦的壓力就會驟增。
所以在我們新寫某個業務流程的時候,要格外注意把握方法的粒度和調用深度,一旦方法的定義粒度過細,就會帶來過多的方法切換,層級越深,就要記住越多的方法context。
這個法則很容易被打破,因為業務總是朝著復雜化的方向發展,一旦業務膨脹,我們不可避免的就要增加方法的個數和加深調用的層數,再怎么罵產品經理是狗,他提的需求你還是得做。所以這條法則需要靠程序員自己的領悟和經驗去把握,去盡量遵循,其目的無非是讓你在下次debug的時候少一些context switch,用更短的路徑去完成調試任務。
繼承的層級<=3
繼承(Inheritance)雖然名列OOP編程的三大特性之一,但老司機一般都會告訴你盡量避免使用繼承,在盡可能多的場景下用組合(Composition)代替繼承。并非繼承本身存在設計問題,而是繼承比大部分人預料中的要難以掌握。在不得不使用繼承的場景下,要反復用Liskov substitution principle去驗證父類和子類設計的合理性,任何一個property是歸屬到子類合適還是父類更好,都需要仔細的斟酌。
繼承的層級要少,越少越好。兩層的繼承大部分人都能簡單的應付,三層的繼承需要合理的設計,請不要超過三層。在設計iOS Controller相關類的時候,三層的類設計類似這樣:
@interface CCViewController : UIViewController
@end
@interface CCAnimatedViewController : CCViewController
@end
@interface MyDetailedViewController : CCAnimatedViewController
@end
CCViewController是你定義的第一層基類,處理所有Controller公共業務邏輯。你的App有非常酷炫的動畫交互,所以你決定在做一層CCAnimatedViewController來處理所有動畫的邏輯(實際上,這一層是否需要值得斟酌),最后一層MyDetailedViewController是具體實現業務的Controller。這已經是三層的繼承設計,試想下如果再加一層基類,這種繼承關系的維護難度會變得如何。
架構的層級<=3
從傳統的PC軟件行業開始,架構師們就已經懂得如何使用分層來設計軟件的架構。分層的設計可以將業務和基礎服務做到合理的解耦,降低軟件維護的成本。分層在降低耦合的同時,也帶來了維護層與層之間通信代碼的開銷,層級越深,邏輯的鏈路就越長,數據傳遞的環節也就越多,顯然這樣會消耗掉開發人員更多的開發調試時間。
現階段經典的移動端架構大致分為三層,分別為Application Layer,Service Layer, Data Access Layer。這種分法經驗過業界大量產品的驗證,能在解耦和額外代碼開銷之間達到很好的平衡。不要輕易嘗試將 單個 項目架構的層級超過3層,當然業務會膨脹,項目會進一步細分,與之對應的代碼量也會增加,代碼的增加可以分為橫行和縱向,橫行代碼的增加限制在某一層之內,縱向的增加即增加架構的層數,我們應該盡可能的去橫向拓展代碼量,也就是增加某一層當中模塊的數量。
以上,全是一家之言,歡迎各位同行共同探討。
via: http://mrpeak.cn/blog/magic3/