從代碼看一個程序員的筆力
從代碼的整潔度上就可以看出一個程序員的實力,規范其實就是讓你養成一種良好習慣的標桿,在此面前我們應該順從。本篇我們以OC為例,統計了一些在編寫程序中需要注意的事項,共有20條,當然還有更多的規范,此處只是做個示例。
單頁代碼最好控制在800行以內,每個方法最好不要超過100行,過多建議對代碼進行重構
相同的邏輯方法定義避免在多個地方出現,盡量將公用的類、方法抽取出來
刪除未被使用的代碼,不要大片注釋未被使用的代碼,確定代碼不會使用,請及時刪除
對其他項目中copy過來的代碼,根據具體需要更新代碼風格,及時刪除未被使用的代碼
項目中所有Group或者文件名稱(圖片名字等),不要使用漢字命名,盡量使用英文命名,國內特有名詞可以使用拼音。
項目中所有Group都需要在項目目錄中存在一個真實的目錄,Group中的文件與真實目錄中文件一一對應。
請在項目中寫必要代碼的注釋
請多使用 #pragma mark - Mark Name 對方法進行分組 。如:
#pragma mark - **********View lifeCycle******
所有類名稱以項目工程開頭命名,如:“JS”(簡書)。針對不同視圖控制器,在末尾添加後綴,如: UIButton 後綴添加“Button"或大家皆知的簡寫,NSArray的變量命名為xxxArray等。
類、方法、屬性等命名,做到見名知意,采用駝峰式命名規則。
根據資源類型或者所屬業務邏輯對項目資源進行分組,使得整個項目結構清晰明了;整個項目保持一種代碼書寫風格。
避免在程序中直接出現常數,使用超過一次的應以宏定義的形式來替代。常數的宏定義應與它實際使用時的類型相一致。如以3.0來定義浮點類型,用3表示整型。 常量的命名應當能夠表達出它的用途,並且用大寫字母表示。例如:
#define PI 3.1415926
當使用條件語句編碼時,不要嵌套if語句,多個返回語句也是OK。
- (void)testMethod { if (![testSome boolValue]) {// 不合適就返回,下面做處理 return; } //Do something important }
當方法通過引用來返回一個錯誤參數,判斷返回值而不是錯誤變量。在成功的情況下,有些Apple的APIs記錄垃圾值(garbage values)到錯誤參數(如果non-NULL),那麼判斷錯誤值會導致false負值和crash。
NSError *error; if (![self trySomethingWithError:&error]) { // Handle Error }
當參數過長時,每個參數占用一行,以冒號對齊。如:
- (void)aboutFisrtNumber:(NSString *)oneStr withNextNumber:(NSString *)twoStr withLastNumber:(NSString *)threeStr{ // do something }
一行很長的代碼應該分成兩行代碼,下一行用兩個空格隔開
self.productsRequest = [[JSProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
刪除多余的空行,所有方法與方法之間空1行,所有代碼塊之間空1行。變量聲明後需要空1行,如果需要分類區別,各類別之間空1行。條件、循環,選擇語句,整個語句結束,需要空1行。最後一個括弧之前不空行。注釋與代碼之間不空行。
#pragma 與方法之間空1行
刪除多余的空行,所有方法與方法之間空1行,所有代碼塊之間空1行。變量聲明後需要空1行,如果需要分類區別,各類別之間空1行。條件、循環,選擇語句,整個語句結束,需要空1行。最後一個括弧之前不空行。注釋與代碼之間不空行。
#pragma 與方法之間空1行
每行代碼最多不得超過100個字。
如果類聲明中包含多個protocol,每個protocol占用一行,縮進2個字符如:
@interface BootViewController : UITableViewController< UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UITextViewDelegate >{ // code }
圖片命名:采用單詞全拼,或者大家公認無岐義的縮寫(比如:nav,bg,btn等);采用“模塊+功能”命名法,模塊分為公共模塊、私有模塊。公共模塊主要包括統一的背景,導航條,標簽,公共的按鈕背景,公共的默認圖等等;私有模塊主要根據app的業務;功能模塊劃分,比如用戶中心,消息中心等。建議背景圖采用以bg作前綴,按鈕背景采用btn作前綴。如:[email protected],[email protected]等。
著手一個新項目條理
在著手一個項目前先讀文檔(如果有文檔的話)。盡管讀了文檔你不一定知道每一個代碼的細節,但是如果你了解那個問題的話,你一定知道怎麼寫可以寫出一個滿足文檔的內容。這個時候大腦裡面就可以有個框架,先猜一猜,然後看代碼,事半功倍。找不到好的文檔,就看他的測試用例,也是有一樣的功效的。因為測試都是從文檔出發編寫的,而不是從代碼出發編寫的。找不到文檔和測試用例?那就直接Gank吧。
讀代碼要層次化、帶著問題去閱讀。首先整體了解這個軟件是干什麼?解決什麼問題?包含哪些大的模塊,各個模塊的作用是什麼,各個模塊的調用關系怎麼樣?然後對於每一個模塊,這個模塊是干什麼的?為什麼要有這個模塊?這個模塊怎麼實現的?最後細化到每一個包,每一個類,每一個函數方法。從上到下,一一擊破每一個問題,認真去思考他這樣設計、寫代碼的好處,因為好的軟件都會滿足這種從抽象->具體的原則的。
在開始讀具體代碼前定位好所有要讀的文件,知道他們的位置和名字,設計良好的工程光看工程的目錄結構和文件名就能知道個大概功能了。事實上閱讀代碼的難易程度70%取決於代碼書寫的規范程度,寫亂掉的代碼,大師也讀不懂。之後根據你對目錄結構的理解確定文件閱讀的順序(我反正都是從main函數開始讀的)。你最好對設計模式有一定了解,否則你讀面向對象的code時會經常無法理解code為啥要弄得這麼層層嵌套。閱讀代碼一個最重要的提升水平的地方就是理解好的代碼如何合理使用設計模式。基本的閱讀起點都會選擇main函數或者類的構造函數。然後把自己想象成cpu執行程序那樣去閱讀你的代碼。遇到需要跳轉函數時,不要急於跳轉,以了解函數功能和輸入輸出為目標,讀代碼最忌諱的是不抓結構抓細節,只見樹木不見森林,比起某個函數具體功能來說對結構的全局把握更重要。功能了解清楚後繼續跳回來(這裡就可以區分代碼寫的優不優秀,優秀的代碼光看函數名字就知道功能,連跳轉都不用)。結構弄清楚了,知道程序怎麼跑了,source code的精華你已經讀了60%了,之後根據需要再對具體函數深入分析,到這裡整個代碼已經被你扒光了,沒什麼神秘了。
閱讀代碼有兩種模式:top-down 和 bottom-up。Top-down 模式,就是先設定一個 use case,比如說打開一個文件。然後靜態跟著代碼看,或者用 debugger 跟著看。每次出現函數調用的時候,把函數的執行層次紀錄下來。大致如下:
func1( ) func2( ) func( ) func3( )
這種圖表很隨意,你可以根據自己的需要增加信息,可以把重要的「實際參數」一直標下來,畫函數調用圖,然後標注每個函數在干什麼。不過這個圖無法清楚地表明一個變量的軌跡,需要另外的圖來標示變量的變化軌跡。要是想提高閱讀代碼的速度,歸根結底要多讀多寫。熟悉程序的基本構成單元(例如循環、分支)的常見寫法,各種lib, api的調用方式。這樣閱讀深層次代碼不用再回頭查形式參數到底指什麼。這個圖的基本作用是防止在閱讀深層次代碼時忘記總體執行層次。Top-down 模式進行到一定層次,往往會發現雖然圖畫了出來,但還是無法了解程序在干什麼。這時需要轉入 bottom-up 模式,一直深入到最底層,給能了解作用的底層函數一個一個的寫文檔。當然這時的文檔是完全底層的觀點。bottom-up的閱讀方法,有時候會一頭扎進去,出不來了。這種方式適合讀一些比較優秀的開源項目的代碼,也會很好地提高內功。然後就是不斷在兩個模式之間轉換,不斷的細化兩種模式的理解。
最後,對於OC工程可以去GitHub找UIViewController-Swizzled這個庫,拉下來放到項目裡,他有什麼用呢?他可以把每個頁面的類名打出來。而且有層次結構,也就是說你只需要打開項目點點點,就知道這個App運行的順序了。
iOS開發的細節及全局觀
"好代碼是廉價的",這句話沒有歧義。中國的語言博大精深,其實這句話的真實含義是,優秀的代碼使用起來毫不費力。通常我們對好代碼的定義是優雅的使用各種設計模式,兼顧各種情況(異常或是正常),有效而且合理的使用優化算法等。很好,這在開發中幫我們做了很多事,這也是在開發中值得發揚的。但是,這難免的會增加實現的復雜性,如果對某些技巧認識不清極有可能成為一種開發的阻礙。實際上,在日常的開發中,我們大多是為了實現功能,很多的都是在寫“廉價的代碼”,以功能優先,先實現功能然後再根據需要在後期進行優化,這沒什麼不好。
其中一個誤區是,我們需要從邏輯著手而不是功能,這樣固然很好,但卻會影響開發的效率。好的程序員和偉大的程序員之間的區別就在於偉大的程序員理解他們的模式,讓代碼廉價。當模式能夠給你帶來好處,而且為你省時時才去使用它們,如果不是這樣就不要使用它們。當框架能夠幫你提高開發速度時才使用它們,
在必要的時候重構,不要做一些超前性的開發。這些只是針對於日常開發,編寫SDK或框架是另一套邏輯,總之就是不要抱殘守缺,固守一種形態,學會因地制宜。
一個不好的現實是大多數程序員都是業務性程序員,每天重復著一樣的工作,寫著功能不同但形式上是一樣的代碼,這就是普通程序員的宿命,因此上面才會為了快速構建而選擇“廉價的代碼”。但是,這不是有進取精神的程序員所想要的,所以我們才會有進步,即使是一個小工也要像專家一樣思考。
那麼,所謂的專家們,在看一個項目時是如何思考的呢,我們下面做一些分析。
優秀的開發者會從架構的視角來看問題,一般而言,軟件系統的架構(Architecture)有兩個要素:
它是一個軟件系統從整體到部分的最高層次的劃分。
一個系統通常是由元件組成的,而這些元件如何形成、相互之間如何發生作用,則是關於這個系統本身結構的重要信息。
架構是一個約定,一個規則,一個大家都懂得遵守的共識。需要強調的是“架構因未來而存在”。架構的最終體現是一個軟件,是模塊化,簡潔,可維護,可任意替換,人性化設計,可以把它全部打碎了重新從一個模型自由的再去組裝成另一個模型。是高內聚,低耦合,既可以作為一個完整的可交付模塊,也可以“打碎”重組。架構需要考慮的是擴展性,安全和性能,如此才算是合理的架構。
在構建項目時,不管采用什麼方法,全局觀、高度的代碼審美能力、靈活使用各種設計模式一定都是貫穿其中的。首先,搞清楚業務邏輯,這決定了你的架構是否足夠易用。另外,傳的參數越少,耦合度相對而言就越小,你替換模塊或者升級模塊所花的的代價就越小。搞清楚業務之間的依賴關系,建立好模塊交流規范並設計模塊,關鍵在於建立一套統一的交流規范。推演預測一下未來可能的走向,必要時添加新的模塊,記錄更多的基礎數據以備未來之需。軟件是有生命的,多一點考慮便會多一分健壯。
好的架構需要下面幾點:
代碼整齊,分類明確,沒有common,沒有core
不用文檔,或很少文檔,就能讓業務方上手
思路和方法要統一,盡量不要多元
沒有橫向依賴,萬不得已不出現跨層訪問
對業務方該限制的地方有限制,該靈活的地方要給業務方創造靈活實現的條件
易測試,易拓展
保持一定量的超前性
接口少,接口參數少
高性能
架構師的第一職責是關注非功能性需求,就是對技術的全面掌控,包括TCP/IP協議,加密解密,計算機原理(增補反碼),JPG碼,MPEG2-3協議,邏輯電子電路,計算機編譯器原理(堆、棧、隊列)等。一些底層的東西,才是技術的核心。“技術人材是當下企業的第一生產力”,在處理業務時學會思考,用架構師的思想去思考,即使是一個小工也要有偉大理想。
敏捷開發
Scrum能吸引我的最大一個原因是它能迫使你敏捷開發;它能迫使你在每個Sprint結束的時候把東西都實現、發布。它不會讓你做出目前用不到的多余的東西;它不會允許你在實現東西上有任何所謂“正確方式”的奢侈行為。在CMM(能力成熟度模型Capability Maturity Model的縮寫,是一種側重於軟件開發過程的管理及工程能力的提高與評估的開發模型)神話崩潰以後,敏捷開發逐漸引起了人們的關注,並被寄予厚望。下面我們就來談一談敏捷開發相關的一些知識。
敏捷開發的起源
我們大部分人都學過瀑布開發模型,它是以文檔為驅動的。因為在瀑布的整個開發過程中,開發人員根據需求文檔進行開發,一切以文檔為依據。敏捷開發(Agile Development)是一種以人為核心、迭代、循序漸進的開發方法,是一種軟件開發的流程,它會指導我們用規定的環節去一步一步完成項目的開發;而這種開發方式的主要驅動核心是人,注重的是人與人之間,面對面的交流;它只寫有必要的文檔,或盡量少寫文檔;采用的是迭代式開發。
敏捷開發提倡將一個完整的軟件版本劃分為多個迭代,每個迭代實現不同的特性。重大的、優先級高的特性優先實現,風險高的特性優先實現。在項目的早期就將軟件的原型開發出來,並基於這個原型在後續的迭代不斷完善。迭代開發的好處是:盡早編碼,盡早暴露項目的技術風險。盡早使客戶見到可運行的軟件,並提出優化意見。可以分階段提早向不同的客戶交付可用的版本。
在每個迭代中,架構師負責將所有的特性分解成多個Story Card。每個Story可以視為一個獨立的特性。每個Story應該可以在最多1個星期內完成開發,交付提前測試(Pre-Test)。當一個迭代中的所有Story開發完畢以後,測試組再進行完整的測試。在整個測試過程中(pre-test,test),基於Daily build,測試組永遠都是每天從配置庫上取下最新編譯的版本進行測試,開發人員也隨時修改測試人員提交的問題單,並合入配置庫。
敏捷開發的一個特點是開放式辦公,充分溝通,包括測試人員也和開發人員一起辦公。基於Story Card的開發方式,團隊會在開放式辦公區域放置一塊白板,上面粘貼著所有的Story Card,按當前的開發狀態貼在4個區域中,分別是:未開發,開發中,預測試中,測試中。Story Card的開發人員和測試人員根據開發進度在Story Wall上移動Story Card,更新Story Card的狀態。這種方式可以對項目開發進度有一個非常直觀的了解。
敏捷開發宣言如下:
個體和交互 勝過 過程和工具
可以工作的軟件 勝過 面面俱到的文檔
客戶合作 勝過 合同談判
響應變化 勝過 遵循計劃
雖然右項也有價值,但是我們認為左項具有更大的價值。
敏捷開發的方式
敏捷開發作為一種指導思想或開發方式,Scrum和XP(Extreme Programming:極限編程)是敏捷開發的具體方式。Scrum和XP的區別是,Scrum偏重於過程,XP則偏重於實踐,但是實際中,兩者是結合一起應用的。
Scrum方式
Scrum的英文意思是橄榄球運動的一個專業術語,表示“爭球”的動作;把一個開發流程的名字取名為Scrum,大家像打橄榄球一樣迅速、富有戰斗激情,運用該流程,你就能看到你團隊高效的工作。
Scrum整個開發過程由若干個短的迭代周期組成,一個短的迭代周期稱為一個Sprint,每個Sprint的建議長度是2到4周(互聯網產品研發可以使用1周的Sprint)。在Scrum中,使用Product Backlog來管理產品的需求,Product backlog是一個按照商業價值排序的需求列表,Scrum團隊總是先開發對客戶具有較高價值的需求。在Sprint中,Scrum團隊從產品Backlog中挑選最高優先級的需求進行開發。挑選的需求在Sprint計劃會議上經過討論、分析和估算得到相應的任務列表,我們稱它為Sprint backlog。在每個迭代結束時,Scrum團隊將遞交潛在可交付的產品增量。Scrum 采用迭代、增量的方法來優化可預見性並控制風險。
Scrum開發流程中的三大角色
產品負責人(Product Owner)
主要負責確定產品的功能和達到要求的標准,指定軟件的發布日期和交付的內容,同時有權力接受或拒絕開發團隊的工作成果。
流程管理員(Scrum Master)
主要負責整個Scrum流程在項目中的順利實施和進行,以及清除擋在客戶和開發工作之間的溝通障礙,使得客戶可以直接驅動開發。
開發團隊(Scrum Team)
主要負責軟件產品在Scrum規定流程下進行開發工作,人數控制在5~10人左右,每個成員可能負責不同的技術方面,但要求每成員必須要有很強的自我管理能力,同時具有一定的表達能力;成員可以采用任何工作方式,只要能達到Sprint的目標。
進行Scrum開發的流程
1.我們首先需要確定一個Product Backlog(按優先順序排列的一個產品需求列表),這個是由Product Owner 負責的;
2.Scrum Team根據Product Backlog列表,做工作量的預估和安排;
3.有了Product Backlog列表,我們需要通過 Sprint Planning Meeting(Sprint計劃會議) 來從中挑選出一個Story作為本次迭代完成的目標,這個目標的時間周期是1~4個星期,然後把這個Story進行細化,形成一個Sprint Backlog;
4.Sprint Backlog是由Scrum Team去完成的,每個成員根據Sprint Backlog再細化成更小的任務(細到每個任務的工作量在2天內能完成);
5.在Scrum Team完成計劃會議上選出的Sprint Backlog過程中,需要進行 Daily Scrum Meeting(每日站立會議),每次會議控制在15分鐘左右,每個人都必須發言,並且要向所有成員當面匯報你昨天完成了什麼,並且向所有成員承諾你今天要完成什麼,同時遇到不能解決的問題也可以提出,每個人回答完成後,要走到黑板前更新自己的 Sprint burn down(Sprint燃盡圖);
6.做到每日集成,也就是每天都要有一個可以成功編譯、並且可以演示的版本;很多人可能還沒有用過自動化的每日集成,其實TFS就有這個功能,它可以支持每次有成員進行簽入操作的時候,在服務器上自動獲取最新版本,然後在服務器中編譯,如果通過則馬上再執行單元測試代碼,如果也全部通過,則將該版本發布,這時一次正式的簽入操作才保存到TFS中,中間有任何失敗,都會用郵件通知項目管理人員;
7.當一個Story完成,也就是Sprint Backlog被完成,也就表示一次Sprint完成,這時,我們要進行 Srpint Review Meeting(演示會議),也稱為評審會議,產品負責人和客戶都要參加(最好本公司老板也參加),每一個Scrum Team的成員都要向他們演示自己完成的軟件產品(這個會議非常重要,一定不能取消);
8.最後就是 Sprint Retrospective Meeting(回顧會議),也稱為總結會議,以輪流發言方式進行,每個人都要發言,總結並討論改進的地方,放入下一輪Sprint的產品需求中;
XP方式
極限編程是一個輕量級的、靈巧的軟件開發方法;同時它也是一個非常嚴謹和周密的方法。它的基礎和價值觀是交流、樸素、反饋和勇氣;即,任何一個軟件項目都可以從四個方面入手進行改善:加強交流;從簡單做起;尋求反饋;勇於實事求是。XP是一種近螺旋式的開發方法,它將復雜的開發過程分解為一個個相對比較簡單的小周期;通過積極的交流、反饋以及其它一系列的方法,開發人員和客戶可以非常清楚開發進度、變化、待解決的問題和潛在的困難等,並根據實際情況及時地調整開發過程。
XP的十三種核心實踐:
團隊協作(Whole Team)
規劃策略(The Planning Game)
結對編程(Pair programming)
測試驅動開發(Testing-Driven Development)
重構(Refactoring)
簡單設計(Simple Design)
代碼集體所有權(Collective Code Ownership)
持續集成(Continuous Integration)
客戶測試(Customer Tests)
小型發布(Small Release)
每周40小時工作制(40-hour Week)
編碼規范(Code Standards)
系統隱喻(System Metaphor)
關於規劃策略:計劃是持續的、循序漸進的。每2周,開發人員就為下2周估算候選特性的成本,而客戶則根據成本和商務價值來選擇要實現的特性。
關於測試驅動開發:編寫單元測試是一個驗證行為,更是一個設計行為。同樣,它更是一種編寫文檔的行為。編寫單元測試避免了相當數量的反饋循環,尤其是功功能能驗證方面的反饋循環。程序員以非常短的循環周期工作,他們先增加一個失敗的測試,然後使之通過。
關於隱喻:隱喻同體系結構是同義詞,隱喻用於描述項目的全貌,Story用於描述個別具體的特征。隱喻是將整個系統聯系在一起的全局視圖;它是系統的未來影像,是它使得所有單獨模塊的位置和外觀變得明顯直觀。如果模塊的外觀與整個隱喻不符,那麼你就知道該模塊是錯誤的
XP的一個成功因素是重視客戶的反饋——開發的目的就是為了滿足客戶的需要。XP方法使開發人員始終都能自信地面對客戶需求的變化。XP強調團隊合作,經理、客戶和開發人員都是開發團隊中的一員。團隊通過相互之間的充分交流和合作,使用XP這種簡單但有效的方式,努力開發出高質量的軟件。XP的設計簡單而高效;程序員們通過測試獲得客戶反饋,並根據變化修改代碼和設計,他們總是爭取盡可能早地將軟件交付給客戶。XP程序員能夠勇於面對需求和技術上的變化。
優秀團隊的選擇
優秀的團隊並不拘泥於某種開發方式的,而是選擇最適合的方式。如果公司的理念恰恰是:開放、協作性強、扁平化團隊,以用戶為中心。那麼使用"敏捷開發方法"恰好暗合,由此也會成為一種榜樣來對其它團隊進行激勵。如果一個團隊一百多人,強求於此也大可未必。
最好的,一定是最合適的。沒有絕對的最好,只有相對的更好。
iOS項目分析及重構
(待續)