這篇文章主要總結一下自己在平時編碼中遇到一些問題的思考。很多時候,程式碼設計很難十全十美,我們面對的往往是選擇題,如何在多個選擇之間權衡取捨,並不是一件簡單的事情。
效能與可讀性“效能”是一個優秀的程式設計師都會注意的點。在寫程式碼的時候,我們往往希望自己能夠以最優的效能寫出滿足需求的程式。但是有時候,一味的高效能可能會降低程式碼的可讀性。
比如在資料庫欄位設計的時候,有一個“年份”的欄位,我們都知道年份一般都是4位數,於是我們使用了SMALLINT來作為這個欄位的型別。使用ORM對映到Java,就是short型別了。
熟悉Java語言的同學應該知道,Java語言在設計數值的運算的時候,會隱式地把它轉換為int型別,而int型別必須要顯式地強轉才能轉成short,所以造成了在程式碼裡有非常多的(short)強轉,如下:
short year = repository.getYearById(id);short nextYear = (short)(year + 1);複製程式碼
這樣的強轉其實並沒有多大的意義,降低的可讀性的同時,並沒有帶來多大的效能提升。
所以我們在寫程式的時候,有時候需要在追求效能和程式碼可讀性方面權衡,在可接受的效能下,儘量讓程式碼更加可讀。
通用性與過度設計如果有多個地方用到相同的邏輯,我們可以考慮把它抽出去,封裝成一個私有方法甚至是一個類。我們稱這段抽取後的程式碼具有“通用性”,也稱為“複用性”。
大家或多或少都了解過一些設計模式,事實上許多設計模式解決的問題正是程式碼的“複用性”。程式碼的複用性很重要,但有時候可能也會帶來複雜性的上升。尤其是在抽取了一些額外的類之後,你的程式碼就會多出很多xxBuilder, xxFactory, xxProxy之類的。更有甚者,直接是xxUtils, xxHelper等不明職責的類,裡面許多雜亂的方法。
公共的方法和類帶來的另一個問題是,如果兩個業務同時用了這一段程式碼,後來需求發生了更改,其中一個業務在這段程式碼裡面的邏輯需要發生一些小的變化,那如果去改公共程式碼,就會影響另一個業務。
所以在“通用性”和“複雜性”之間怎麼權衡?答案就是“簡單設計”,不要“過度設計”。
在寫程式碼的時候,儘量讓程式碼簡單,不為了複用性去抽取很多的方法和類,只為了可讀性去做必要的重構。只有到了必要的時候才抽取公共的方法或類,比如多個業務確實用到了同一段複雜邏輯,且大概率不會發生更改。
程式碼是這樣,介面亦是這樣。不要因為考慮到“以後可能會用到”就去過度設計,到時候做到了那個功能再重構不遲。
至於具體怎樣去實施?我認為沒有一個明確的準則,需要多寫程式碼,多思考,多了解業務,才能敏銳地判斷是否需要抽取。
寫多少測試?筆者平時專案上都是遵循的TDD流程,所以寫的測試還算比較多。在我們專案上,儘量是優先寫單元測試,然後整合測試只寫必要的happy-path,有些比較簡單的類甚至可以不寫整合測試,比如controller層。先寫測試,再寫實現,測試驅動出實現程式碼。
《重構》這本書裡面講到,寫測試不應該是一個固定的,死板的開發流程,我們寫測試的時候,應該只寫必要的測試,只寫那些我們沒有十足的信心的程式碼的測試。
但這似乎與TDD的“測試先行”的觀點有些相違背。如果我們先寫測試,那怎麼能知道我們的實現程式碼長怎樣呢,又怎麼能知道我們有沒有十足的信心呢?
其實在實際的專案開發中,測試想要100%覆蓋是十分困難的。尤其是在“白名單”這種情況下,我們往往只能測出白名單內的引數是符合條件的,但不可能測完所有的白名單外的引數是不符合條件的。所以只能選一兩個去測試。
有時候想要先寫測試是十分困難的。比如我們在實現的時候可能會依賴其它的類,所以我們在測試的時候可能會去mock這個類,但在寫實現程式碼的時候,發現這個類不合適,可能需要換成呼叫另一個類。這個時候又回過頭來改測試,就顯得有些麻煩。所以有時候並不一定是完全的測試先行,而是形成了一個環,測試-實現-重構。
總結上面講到的是筆者對日常開發中遇到的一些問題的思考。其實有時候也得要根據實際情況去考慮,在寫程式碼的時候,需要考慮的東西很多,比如可讀性、可維護性、可擴充套件性、效能、測試等等。
程式設計是一項工程,如何把這件工程做好,就需要我們掌握更多的知識。多研究一下別人是怎麼寫程式碼的,它們有什麼優劣,看得多了,總結得多了,“經驗”自然就有了。
推薦書籍:
《重構》《程式碼整潔之道》《Effective Java》《設計模式》《實現領域驅動設計》認真寫文章,用心做分享。
個人網站:yasinshaw.com