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