前幾天在“TDD和重構練功房”中看到熊老師發了一篇文章叫做"長函式,有如王大媽的裹腳布",這篇文章認為長函式是一個 bad smell,同時對函式長短的標準進行了量化:
一個常見的原則是將方法的行數控制在 5 行之內——《軟體開發沉思錄》
5 行之內這一標準簡直讓我感到恐怖,對,不是驚訝,真的是恐怖的感覺。 5 行以內的描述空間,這怎麼可能,稍微複雜一點的函式何止幾十行...,深呼吸,冷靜一下,再想想,這一規則背後到底還有什麼我不知道的,於是就有了以下對話:
lyning(我):我沒看錯吧,居然不是 5 行左右,是 5 行之內?
劉光聰:@lyning 這很正常,這幾年遇到的專案(新專案,ai領域)大多都控制在5行以內。都是在@熊節 2011年洗腦之後開始實踐的。
lyning(我):感覺控制函式行數少是一種使程式碼逼近單一職責的手段。
熊節:單一職責和恰當的抽象層次。
熊節:麥肯錫方法,講事情永遠是3-5個點。
lyning(我):熊老師,恰當的抽象層次,怎麼理解這句話呢?
熊節:就…比如我跟你講一個事,我知道這個事1分鐘講不完,要講5分鐘這麼個複雜度
熊節:我就一定會分成一二三點,每一點我會強調一下,再具體講裡面的內容,講完以後我會總結一下我講了三個點。
熊節:跟一個幾十行的函式是一樣的。
lyning(我):有點明白了,把大問題要拆分成小問題,拆分出來的這幾個小問題都屬於這個大問題層面的東西。
熊節:仝老師有個套路,就是每個問題你始終把它描述成 輸入-處理-輸出 三句話.
劉光聰:
劉光聰:一個小的c++例子,抽象層次分明。
...
從上面可以對話中可以得到以下關鍵字:
- 單一職責
- 恰當的抽象層次
熊老師採用比喻描述了什麼是恰當的抽象層次,劉老師視覺化了該概念在程式碼中的體現。可以看到一個 10 多行的程式碼被重構到 5 行秒懂的函式,看來這個標準還是可行的嘛。
其實該原則出自《軟體開發沉思錄》物件健身操一節,更“恐怖”的是,物件健身操還列出 9 個堪稱“變態”的原則:
- 方法只使用一級縮排(One level of indentation per method)
- 拒絕使用else關鍵字(Don’t use the ELSE keyword)
- 封裝所有的原生型別和字串(Wrap all primitives and Strings)
- 一行程式碼只有一個“.”運算子(One dot per line)
- 不要使用縮寫( Don’t abbreviate)
- 保持實體物件簡單清晰(Keep all entities small)
- 任何類中的例項變數都不要超過兩個(No classes with more than two instance variables)
- 使用一流的集合(First class collections)
- 不使用任何Getter/Setter/Property(No getters/setters/properties)
還能更短
函式的第一規則是短小。第二條原則是還要更短小。
函式應該只做一件事。做好這件事。只做這件事
上面的描述出自 Bob 大叔的《程式碼整潔之道》,對於函式應該多小,Bob 大叔提到了他看了 Kent Beck 寫 的一個程式,函式都是隻有三到四行,非常簡潔易懂。最終 Bob 大叔對函式應該多長下了一個定義,他給出了一個只有 3 行函式體程式碼的示例,表示函式就應該這麼短。這又一次顛覆了我的價值觀!
單一職責
單一職責是 SOLID 中的首要原則,通過它可以引出其它原則。可以發現函式越短,那麼它能做的事情就越少,就越能做好這件事情,也就越能驅動出單一職責的函式。
恰當的抽象層次
對於 add()
這個函式而言,if (readonly)
、 atCapacity()
、grow()
和 addElement()
這 4 個邏輯體現了同一抽象層次,因此 add()
函式還是隻做一件事,畢竟編寫函式是為了把一些大的概念拆分成另一個抽象層次的一系列步驟 。
如果函式體現了非常多的細節,或者處於不同的抽象層次,這對於讀者來說簡直是災難,不僅如此,亂其實還暗示了作者在當下同樣也是思維混亂。
程式設計與思維
軟體開發就像是一個口耳交替的遊戲,獲取準確的一手資訊不容易,把問題描述清楚同樣也不容易。筆者現在覺得程式設計是一項非常重視描述技巧的活動,表達得是否清楚很大一部分源於人的思維是否清晰(結構化),也許程式設計只是基本功,思維才是內功,畢竟把問題搞複雜,比弄清晰要簡單得多。
良好的描述技巧
要把邏輯抽象化,必須知道某段邏輯在做什麼,並且對函式進行恰當的描述,這裡的難點之一在於它需要良好的描述技巧,好在描述技巧其實也有一些模式和規律可循,厲害的人已經幫我們提煉了部分模式,可以參考“名正則言順:聊聊“起名”這件事”
秒懂
上面談了一些模模糊糊的概念,而秒懂應該是短函式的一個最直觀的體現。不過不僅僅讓自己秒懂,還要讓讀者秒懂,因此寫好了程式碼,應該分(e)享(xin)給其他的小夥伴,看看小夥伴們的反饋,在反饋中不斷改善程式碼。
如果還覺得不過癮,那再看看這個函式,給自己計時看看需要多長時間讀懂:
然後再看看這個:
總結
筆者認為,短函式的實際價值在於容易維護。處於同一抽象層次的短函式,做的事情越少,需要被理解的概念也就越少,就越不容易出現認知超載。需要強調的是,短函式、恰當的抽象層次和良好的描述,對於大部分人來說,不是一下子就冒出來的,而是被打磨出來的,怎麼打磨呢?當然是 TDD。
歡迎關注我的微信訂閱號,我將會持續輸出更多技術文章,希望我們可以互相學習。