一千個讀者,就有一千個哈姆雷特,代碼質量也一樣如此。前端
想必每個對於代碼有追求的開發者,對於「高質量」這個詞,或多或少都有本身的一絲理解。當我在長沙.NET技術社區羣拋出這個問題時,衆說紛紜。有人說註釋齊全、可讀性高,就是高質量;有人說變量命名、代碼層次清晰,就說高質量的代碼;有人說那些使用了新特性的代碼,不少都是高質量代碼;也有人說,高質量的代碼是個僞命題,由於他每每要花大量的精力才能精心打磨,有這個時間,產品早就黃了。vue
說到」高質量「代碼,就不得不提」整潔代碼」。這個概念來源於暢銷書《代碼整潔之道》(The Clean Code)中,鮑勃大叔引入了這個整潔代碼的概念。程序員
他認爲:算法
寫整潔代碼,須要遵循大量的小技巧,貫徹艱苦習得的‘整潔感’」,這種「代碼感」就說關鍵所在。有些人生而有之。有的人費點勁才能獲得。它不只讓咱們看到代碼的優劣,還予咱們以借戒規之力化優爲列的攻略。vuex
缺少」代碼感」的程序員,看混亂是混亂,無處着手,有「代碼感」的程序員,能從混亂中看出其餘的可能與變化。「代碼感」幫助程序員選出最好的方案,並指導程序員指定修改行動計劃,按圖索驥。c#
編寫整潔代碼的程序員就像藝術家,他可以用一系列變化把一塊白板變做由優雅代碼構成的系統。後端
這本書值得擺在每一位程序員的案頭。許多熱衷於英文原做的讀者都會說國人翻譯的許多做品都失去了原做的韻味,但這本韓磊老師翻譯這本中文版十幾年過去了,印刷了許多版了,也能客觀證實這本譯做的價值。設計模式
也許初讀這本書,許多做者提到的手法咱們沒法短期內認真體會,但許多讀過這本書都表示,許多想法在咱們寫代碼的時候忽然迸濺而出,使得思路可以更加通達,並達到一種「人碼合一」的狀態。安全
在咱們大部分開發者看來,咱們開發的代碼,每每無需涉及過於複雜的業務邏輯或底層技術,只需簡單的使用一些代碼拼湊,便可按時完成咱們的任務,也就說所謂的」CRUD業務開發者「。架構
但業務系統自己也並不是全靠所謂的「無代碼平臺」或「代碼生成器」可以自動開發完成,他依然須要開發者用心去設計其中的邏輯、變量、結構、流程,才能更好的運轉,尤爲是要想讓應用系統可以保持長久的生命力,更須要咱們可以編寫更高質量的代碼。
在《代碼整潔之道》中,做者將這種編寫高質量代碼的能力,稱爲「代碼感」,這種感受有時須要靈光一現,有時又須要花費大量的精力才能完成。
就像在《灌籃高手》中,安西教練讓你們培養球感:
兩萬個球?寫兩萬個類/方法/代碼行?確實是一種提升」代碼感「的好方法。
但跟投球要掌握方法同樣,簡單的重複寫兩萬行代碼估計很難提升代碼質量,依然須要大量刻意練習才能帶來質量上的提高。
而如何編寫高質量代碼,在軟件開發領域,也有一些前人總結出來的良好準則,人們將這些準則,總結爲「設計原則」。除了設計原則外,還要許多良好的實踐模式,人們將它們稱爲」設計模式「。設計原則就像是內功心法,設計模式,則像招數功夫。
也許咱們沒法徹底遵循這些原則或模式,但可以靈活的運用,總能給代碼質量帶來提高。
我我的認爲:高質量代碼是可讀性強、易於測試,它們可以恰如其份的表達業務的須要,並能根據業務須要易於修改的代碼。 高質量的代碼也許與技術架構、特定API、特定的語言沒有太大關係,但高質量代碼或許都具有一些類似的特色。
結構是代碼的核心,就像高樓的支架,爲整個代碼的完整運行奠基基礎。好的代碼必定結構清晰,讓人易於理解,並能快速定位問題、解決問題。
有人說好文章的結構特色即是:」 鳳頭、豬肚、豹尾「, 文章的起頭要奇句奪目,引人入勝,如同鳳頭同樣俊美精采;文章的主體要言之有物,緊湊而有氣勢,如同豬肚同樣充實豐滿;文章的結尾要轉出別意,宕開警策,如同豹尾同樣雄勁瀟灑。 代碼也許無需追求達到這麼高的境界,但遵循必定清晰的代碼結構也能達到一樣的效果。
結構按照我我的的理解,可能包括如下幾種層面:一、項目文件夾命名;二、分層;三、模塊命名;四、代碼格式。
對於複雜項目,打開文件夾和解決方案的第一眼,是清晰仍是紊亂,每每就是咱們對於項目的第一印象。許多資深研發工程師,都會傾向於用數字來對文件夾進行編號,例如對於複雜項目,咱們使用以下命名方式對定義解決方案文件夾,雖然不會花特別多的功夫,但會給開發過程帶來許多便利。
固然,因爲在Visual Studio中,項目文件夾自己屬於sln解決方案文件中定義的層級結構,並不會在資源管理器文件夾中體現,因此有時還須要在資源管理器文件夾中也定義相似的層級結構。
01 基礎服務 02 框架服務 03 應用服務 01 工做流服務 02 權限服務 03 日誌服務
分層式架構你們都習覺得常,其中尤爲以三層架構(用戶表現層,業務邏輯層,數據訪問層)已經深刻人心,成爲許多.NET開發者的廣泛承認,而領域驅動設計最多見的則是四層式領域驅動設計(用戶界面層,應用層,領域層,基礎設施層)。
分層式架構體現了」關注度分離「的原則,在進行軟件開發過程當中,能夠根據需求,找到對應的邏輯分層,進行代碼實現;有時不一樣邏輯分層的組件會以各自不一樣的發展速度迭代以知足不一樣的需求;在適當的狀況下,還能採用分佈式架構,讓不一樣層運行在不一樣的基礎設施中,期間經過rpc等方式保持通訊,給架構留下了足夠的彈性空間。
設計分層式架構並不是越多越好,儘可能控制在三到四層就足夠了,否則會陷入」千層餅「的陷阱,過多的分層和過少的分層,其實沒有任何區別。
對於後端工程師來講,理解分層式架構並不困難,難的是要識別哪裏邏輯代碼應該歸屬於哪一層;而許多對於方興未艾的前端技術來講,如何分層,卻彷佛並非一件容易的事,因爲前端業務要適應來自用戶層面的無窮變化,很容易就陷入「意大利麪」式的代碼混亂中。vuex框架爲前端開發者提供了一種良好的示例,有時無需深刻了解vuex的機制,只需"模仿"這種分層方法,就能寫出更加易於維護的前端代碼了。
在.NET開發中,模塊有時是一個獨立的項目,並以一個獨立dll(類庫)的形式進行分發。模塊也是最爲常見的一種代碼實踐,但在《領域驅動設計·軟件核心複雜性應對之道》一書中,做者埃裏克·埃文斯卻指出模塊的運用,引發了「認知過載」的問題:
認知負荷理論認爲,在問題解決和學習過程當中的各類認知加工活動均須要消耗認知資源,若全部活動所需的資源總量超過個體擁有的資源總量,就會引發資源的分配不足,從而影響個體學習或問題解決的效率,這種問題就說「認知過載」。
這段理論確實有點拗口,對應到軟件開發過程當中,用通俗的說法,就是這個包承載的知識量太大了,把本來能夠分離到多個模塊中的邏輯代碼都囊括進來,使得其反而下降了開發的效率。
尤爲是類庫的定義,不一樣的開發者有不一樣的習慣,有時按技術來劃分,有時又按業務場景來劃分,有時分拆,有時組合,「千人千面」,不連貫的設計思想,和「能用就行」的想法混合在一塊兒,很容易就形成了一鍋粥的狀況。
在.NET項目中,每用一個using,就引入了一種耦合,而使用了new方法,建立了一個對象的示例,又引入了一個對象的耦合。
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using xxx.Core; using xxx.Infrastructure.Extension; using Google.Protobuf.Collections; using Google.Protobuf.WellKnownTypes; using Grpc.Core;
而設計優良的代碼模塊,則可讓依賴儘量的減小。
模塊其實也是實踐「高內聚,低耦合」思想的主要陣地,若是業務相關性很高的對象被劃分到不一樣的模塊中,每每會使得開發者很難理解它們在業務上的做用,也會致使模塊間的耦合進一步提升。
所以,好的模塊設計應該將那些具備緊密概念關係的模型元素集中在一塊兒,並能描述該模型元素的職能,使之成爲一個內聚的概念集合。
關於如何設計模塊,在《敏捷軟件開發 原則、模式與實踐》一書中,做者引述瞭如下設計原則基於粒度這個角度爲組件的內聚性進行描述:
重用的粒度就是發佈的粒度。REP指出,一個組件的重用粒度能夠和發佈粒度同樣大。咱們所重用的任何東西都必須被髮布和跟蹤。簡單的編寫一個類,而後聲稱它是可重用的作法是不現實的。只有在創建一個跟蹤系統,爲潛在的使用者提供所須要的變動通知、安全性以及支持後,重用纔有可能。
一個組件中的全部類應該是共同重用的,若是重用了組件中的一個類,那麼就要重用組件中的全部類。
組件中的全部類對於同一種性質的變化應該是共同封閉的。一個變化如果對一個封閉的組件產生影響,則將對組件中全部的類產生影響,而對其餘組件則不形成任何影響。
從穩定性的角度爲組件的內聚性進行描述:
在組件中的依賴關係圖中,不容許存在環。
朝着穩定的方向進行依賴。
設計不能是徹底靜態的。要使設計可維護,某種程度的易變性是必要的。咱們經過遵循共同封閉原則來達到這個目標。使用這個原則,能夠建立對某些變化類型敏感的組件。這些組件設計爲可變的。咱們指望他們變化。
組件的抽象程度應該與其穩定程度一致。
代碼格式,就是一個C#代碼文件的邏輯結構。寫代碼實際上是一件成本很低的事,但維護代碼,倒是一件成本很高的事。開發一個功能,只需短短几十分鐘時間,但若是咱們要去找出代碼中存在的缺陷,卻每每須要花費大量的時間。
這就客觀上要求,咱們書寫的代碼應該儘可能方便閱讀(可讀性)、檢索(快速找問題)、易於維護,而書寫出「格式化」的代碼,大概是咱們可以提升代碼質量的第一步。
對於書寫的代碼,大部分都是從上往下閱讀,在須要閱讀的代碼較多量時,每每會選擇摺疊到定義,這樣就能一眼看出每一個方法的用途,要達到這個效果,就意味着咱們須要精心設計安排代碼的垂直格式。有經驗的開發者每每會按照這種結構。
私有字段:定義類內部的基本成員,高層次概念,常量,和引入的算法。
構造函數:定義類的建立過程。
公共方法:定義類爲外部暴露的行爲。
私有方法:定義類爲內部提供的行爲。
在《代碼整潔之道》這本書中,做者介紹了他對於代碼的格式要求:
代碼文件的長度控制在200-500行左右,且短文件一般比長文件易於理解。垂直閱讀時,頂部是粗線條概述,隱藏了故事細節,而後再不斷展開。
每行展現一個表達式或一個子句,尤爲是C#的鏈式語法,儘可能一行代碼就是一個方法。
entity.Property(e => e.Memo) .HasMaxLength(500) .IsUnicode(false) .HasComment("備註");
每組代碼行展現一個完整的思路,思路間用空白行隔開。垂直方向上,靠近的代碼能夠展現它們之間的緊密關係,可以讓代碼更好閱讀。
變量聲明應儘量靠近其使用位置,由於函數很短,本地變量應該在函數的頂部出現。一個函數調用了另一個函數,應該把它們放到一塊兒,且調用者應該在被調用者上面。概念相關的代碼應該放到一塊兒,相關性越強,彼此之間的距離就該越短。
橫向首先表如今代碼的寬度上,儘可能控制在一行代碼不超過120個字符。
水平方向上,能夠用空格字符把彼此緊密相關的變量或對象鏈接在一塊兒,也能夠用空格將相關性較弱的對象分割開。
注意水平縮進和左對齊,尤爲是上面提到的鏈式語法,若是點號沒對齊,簡直讓人難受。
entity.Property(e => e.UserId) .HasMaxLength(10) .IsUnicode(false) .IsFixedLength();
本文對如何編寫高質量代碼進行了一些簡單的概述,介紹了代碼的分層、組件(包)的設計、以及整潔代碼中的一些開發實踐,經過了解這些知識,可以讓咱們逐漸造成本身對於代碼的體會,並經過不斷的練習,將可以提升咱們的代碼能力。
固然,有時,寫文檔、適當的作一些軟件工程設計,看起來與完成代碼編寫無關,但也一樣是提升代碼質量的一種手法,經過就像許多好文章每每會先搭框架,好代碼也一樣如此。
根據業務須要,畫一波流程圖、領域模型圖、類圖、時序圖可以讓咱們的思路提早沉澱,讓咱們的開發過程更加流暢,更能開發出高質量的代碼。
下一篇,將對規範命名、註釋、設計向量、設計原則、設計模式進行一些討論。