自從做Team Leader之后,,身上權(quán)責(zé)發(fā)生了變化,,于是讓我煩惱的不再是具體某個(gè)功能,某個(gè)界面的實(shí)現(xiàn),,而是如何在現(xiàn)有代碼的基礎(chǔ)上做漸進(jìn)式的改進(jìn),,創(chuàng)造出比較合適規(guī)范和框架,使得組內(nèi)成員更快更好地完成任務(wù),。一年下來,,頗有點(diǎn)想法,,于是啰嗦幾句關(guān)于iOS App開發(fā)的那些事。 合適的人 首先明確一點(diǎn),,合適的人是指純技術(shù)團(tuán)隊(duì)的建設(shè),。一支戰(zhàn)斗力再強(qiáng)的技術(shù)團(tuán)隊(duì),面對(duì)一個(gè)朝三暮四,,分分鐘推翻自己原有想法的產(chǎn)品經(jīng)理/項(xiàng)目經(jīng)理,,再好的戲也唱不出來?;◣讉€(gè)月加班加點(diǎn)做項(xiàng)目,,還沒發(fā)布,直接推翻重做,,這時(shí)候你就得去樓下ATM機(jī)看看卡內(nèi)余額了:余額夠了,,收拾收拾好找下一家了。 計(jì)算機(jī)界有句名言:計(jì)算機(jī)相關(guān)的所有問題都可以通過增加一個(gè)額外的抽象層來解決,。但是軟件開發(fā)卻不是這樣:增加層(人手)在一定程度上可以加快開發(fā)進(jìn)度,,當(dāng)過了某個(gè)閾值后其效果就顯得不是那么明顯,甚至?xí)鸱葱Ч?。?duì)于一個(gè)項(xiàng)目而言需要的往往不是更多的成員,,而是適量的合適成員。每一個(gè)人因?yàn)椴煌慕逃尘?,從業(yè)背景,,項(xiàng)目經(jīng)歷(技術(shù)選型,學(xué)習(xí)經(jīng)歷,,項(xiàng)目管理)對(duì)程序開發(fā)都會(huì)有不同的理解和思維模式,。反應(yīng)在業(yè)務(wù)上就是各種各樣的代碼風(fēng)格。 舉例來說:有些人恨不得把所有單一功能都一一獨(dú)立出來封裝成類,,而有些人卻喜歡一個(gè)大類洋洋灑灑寫上上千行,。大部分情況下我都是傾向于前者,但是就像我時(shí)常吐槽的那樣:It depends,。不僅僅是軟件開發(fā),,幾乎所有的事到最終都會(huì)歸結(jié)到一個(gè)統(tǒng)一的問題上:怎樣才是一個(gè)度,? 一群理念相去甚遠(yuǎn)的人在一起工作是件異常痛苦的事:相當(dāng)一部分的時(shí)間會(huì)浪費(fèi)在解釋,,爭論和排遣由此帶來的沮喪和憤怒上。古人語:道不同,,不相為謀,。但到了真正的工作中卻不能如此隨性,缺乏足夠動(dòng)力的老人,,能力出眾的技術(shù)骨干,,干勁十足卻缺乏經(jīng)驗(yàn)的新人都需要互相體諒,,學(xué)習(xí)和磨合。所以大部分的創(chuàng)業(yè)團(tuán)隊(duì)的技術(shù)團(tuán)隊(duì)因?yàn)槔砟钕嘟?,往往效率?huì)足夠高,,而大公司內(nèi)的開發(fā)小組卻永遠(yuǎn)無法達(dá)到那樣的效率,更需要相應(yīng)的規(guī)范和程序框架,。 得出上面這個(gè)結(jié)論的另一個(gè)理由是我對(duì)人的可塑造性是持悲觀態(tài)度的:多數(shù)人并沒有跳出自己思維局限性的意愿,,動(dòng)力和能力。少數(shù)人在沒有任何外界壓力的情況下仍會(huì)不斷總結(jié)學(xué)習(xí)進(jìn)步(主動(dòng)學(xué)習(xí)型),,而其余的人要么沒有任何意愿,,關(guān)心的只是完成任務(wù)和拿到工資而已,要么想要進(jìn)步而不得法,。而你的團(tuán)隊(duì)不可能全由主動(dòng)學(xué)習(xí)型的成員組成,,這時(shí)候規(guī)范和程序框架的引入才能夠讓各種類型的人更好的合作。 合適的規(guī)范 大家都理解軟件開發(fā)需要合適的規(guī)范:代碼規(guī)范,,程序規(guī)范,,流程規(guī)范等等,以此來減少意外的出現(xiàn):最少驚訝原則,。但在實(shí)際執(zhí)行中卻會(huì)碰到各種情況,,其中最大的問題是:怎么鑒別哪些規(guī)范是需要強(qiáng)制執(zhí)行,那些規(guī)范是推薦執(zhí)行,。規(guī)范的強(qiáng)制執(zhí)行帶來的是代碼的可讀性提升和二義性減少,,而壞處也是顯而易見的:對(duì)于大部分有想法的程序員而言這種規(guī)定太死板,容易引起抵觸心理,,產(chǎn)生不安定因素,。這種情況常見于各種標(biāo)準(zhǔn)的外包公司。而如果大部分的規(guī)范設(shè)定為推薦執(zhí)行,,在沒有良好的引導(dǎo)下,,規(guī)范容易被忽視。 網(wǎng)上有很多關(guān)于ObjC的代碼規(guī)范,,比如蘋果自家的規(guī)范和《Google Objective-C Style Guide》等,。這些規(guī)范一般只有兩種分級(jí):推薦和不推薦。而我更推薦把代碼規(guī)范分成五個(gè)等級(jí):強(qiáng)制要求,,強(qiáng)烈推薦(但不強(qiáng)制),,良好,可接受和不可接受,。以下僅舉部分例子加以說明,。 1. 符合蘋果規(guī)范的命名方式。 a.類名開頭大寫,,方法和變量名以駝峰法命名,。強(qiáng)烈要求,,這沒有什么好說的,蘋果系統(tǒng)類庫和絕大多數(shù)的第三方開源庫都是如此,。但在部分蘋果的sample中也看到過用m做前綴表示類成員變量的寫法,,這些都是屬于遺產(chǎn)代碼的問題,仍舊是可接受范圍,,但是自己代碼內(nèi)部使用類似匈牙利的命名法就是不可接受,。 b.類名使用至少三個(gè)字符做前綴,內(nèi)部方法使用兩個(gè)下劃線做前綴,。強(qiáng)烈推薦,。上面的做法可以最大程度避免和系統(tǒng)類庫發(fā)生重名的情況:因?yàn)樘O果宣稱保留所有兩位字符前綴的使用權(quán),同時(shí)其內(nèi)部方法命名以一個(gè)下劃線做前綴,。 c.無論使用K&R Style還是Allman Style都是可接受的范圍,,但是強(qiáng)烈推薦在一個(gè)文件內(nèi)保持一種形式。 d.在保證代碼可讀性的基礎(chǔ)上保持代碼的簡短和統(tǒng)一性:小類,,小方法,,統(tǒng)一的函數(shù)返回。小類,,小方法可以保證他人閱讀時(shí)更方便地關(guān)注類邏輯,,而不是具體細(xì)節(jié),而統(tǒng)一的函數(shù)返回可以減少意外錯(cuò)誤和降低錯(cuò)誤排查的難度,。而保證代碼的簡短和不羅嗦也是很重要一點(diǎn),,經(jīng)常會(huì)看到如下代碼:
真心無法直視。 2. 良好的代碼/工程結(jié)構(gòu) a.為整個(gè)工程創(chuàng)建worksapce,。 b.按照權(quán)責(zé)分門別類存放資源文件:每種類型的資源存放于獨(dú)立的目錄下:圖片,,聲音,配置文件等等,。而圖片又可以按照類型分別存放在不同的子目錄下:全局類型,,背景圖,logo,,登錄等等,。 c.合理的代碼結(jié)構(gòu)。推薦如下的工程目錄結(jié)構(gòu) Core:工程內(nèi)一些通用的機(jī)制實(shí)現(xiàn)類:統(tǒng)一的任務(wù)管理,,模塊管理,,服務(wù)管理。 General:公用類和方法,,包括工程內(nèi)ViewController,UITableViewCell基類(Base),,公用Category(Category),公用UI組件(CustomUI),,公用輔助方法(Helper)和宏定義(Marco),。 Model:公用數(shù)據(jù)模型 Sections:不同程序單元。如登錄,,設(shè)置等等,。其下又可以按照功能分成不同的子目錄:當(dāng)前單元使用的自定義UI組件,管理類,,數(shù)據(jù)模型和ViewController等等,。 Vendors:第三方庫。 合適的框架 一個(gè)合適的框架不是銀彈,,在我看來框架要解決的問題從來不是:有了框架之后,,工程就能無比正確地進(jìn)行下去。好的框架能夠做到的事僅僅只是:降低通用問題的復(fù)雜度和減少發(fā)生錯(cuò)誤的可能性,。個(gè)人認(rèn)為一個(gè)良好iOS App框架應(yīng)該是有如下特點(diǎn): 1. 定義清晰的層次結(jié)構(gòu),。橫向上,各模塊互相獨(dú)立,,僅通過有限的幾個(gè)接口進(jìn)行通訊,。最理想的狀態(tài)是除核心模塊外,其他模塊都是可拔插,??v向上,各層次間依賴關(guān)系清晰,,基本不出現(xiàn)逆向依賴的情況,。 橫向模塊一般依賴于業(yè)務(wù)需求,常被定義成各種Service或Manager,。一種好的做法是有個(gè)統(tǒng)一的Service管理器負(fù)責(zé)相應(yīng)Serivce的加載,,卸載,監(jiān)聽和分發(fā)App級(jí)別的通知給相應(yīng)Service,,如前后臺(tái)切換,,收到內(nèi)存警告等。這樣做一方面容易實(shí)現(xiàn)上面說的模塊的可插拔化,,另一方面也保證了公用特性處理的一致性,。在這方面微信就做得不錯(cuò),基本所有的模塊都是從MMService繼承而來,,由MMServiceCenter進(jìn)行管理,。當(dāng)然從dump出來的頭文件也可以發(fā)現(xiàn)一些管理上的紊亂,比如一些ViewController都是繼承自MMService,。 縱向的層次劃分基本各個(gè)App不會(huì)有太大區(qū)別,,一般可以分為三個(gè)層次: a.展現(xiàn)層(Presentation layer),負(fù)責(zé)管理UI和UIViewController b.邏輯層(Business/Service Layer),,負(fù)責(zé)邏輯數(shù)據(jù)的定義和轉(zhuǎn)發(fā),,起到承上啟下的作用,。 c.數(shù)據(jù)訪問層(Data Access Layer),負(fù)責(zé)具體API構(gòu)造,,網(wǎng)絡(luò)請求,,數(shù)據(jù)持久化等。 各層根據(jù)業(yè)務(wù)邏輯的復(fù)雜性內(nèi)部又會(huì)使用單層或者多層結(jié)構(gòu),。以數(shù)據(jù)訪問層為例,,一般又可以細(xì)分為網(wǎng)絡(luò)層,持久化層,。而一般而言,,展現(xiàn)層(UIView和UIViewController)都是直接使用邏輯層提供的Model進(jìn)行展現(xiàn),但是某些場景下往往需要不同的Model有相同的界面展示(如我們的App易信中,,會(huì)話界面,,收藏界面,問一問功能都需要進(jìn)行圖片的展現(xiàn),,但這三個(gè)模塊下的Model定義并不一致),,這就需要增加額外的ViewModel層用于粘合展現(xiàn)層和邏輯Model。 2. 遵守SOILD原則和慎用各種設(shè)計(jì)模式,。這是個(gè)老生常談的話題了,,并不是iOS開發(fā)獨(dú)有,展開講可以講上幾天幾夜,,不贅述,。 3. 定義自己的UI基類:UIView,UIViewController,,UITableviewCell,。這一點(diǎn)的好處不言而喻,所有的子View,,Controller,,Cell都能夠很方便的繼承基類的共有的行為,樣式,。但也會(huì)引進(jìn)很大的管理風(fēng)險(xiǎn):組內(nèi)成員總會(huì)經(jīng)不起誘惑往基類塞各種并不普適的特性,,引起基類權(quán)責(zé)的無限膨脹。大基類不僅增加組內(nèi)成員對(duì)代碼的理解難度,,同時(shí)也增加出現(xiàn)問題時(shí)的排查難度,。從這方面講,微信的UIViewController基類設(shè)計(jì)就極端失?。篗MUIViewController的屬性和方法數(shù)幾乎過百,。 4. 提供方便好用的工具類。一些好用的工具類往往會(huì)成為框架重要的有機(jī)組成部分,方便快捷地解決局部問題,,同時(shí)又步引入過多的復(fù)雜度,。NSTimer的retain cycle是個(gè)很容易掉去的坑,那么提供一個(gè)基于Block或者weak delegate的NSTimer的封裝就是不錯(cuò)的選擇,。使用KVO容易發(fā)生add和remove的不配對(duì)調(diào)用,,那么就引入THObserversAndBinders或者FB的KVOContorller,。某些核心模塊需要被多個(gè)模塊依賴時(shí),,引入類似XMPP的GCDMulticastDelegate就能夠方便地進(jìn)行解耦。 5. 好的范例,。在前幾年使用C++的那段暗無天日的日子里,,我常想的一個(gè)問題是:如何在API層面去限制和規(guī)避一些錯(cuò)誤。比如往線程池里面扔的task必須是堆上分配的對(duì)象,,那么如何去強(qiáng)制傳入的指針指向的是堆地址而不是棧地址呢,?這種傻問題大部分情況下是無解的,有時(shí)候有解卻是個(gè)異常別扭的解,。而現(xiàn)在我更相信破窗理論所提供的可能性:做好示范,,接下來的一切都會(huì)水到渠成。 |
|