摘要:在代碼整潔之道,提出一種軟件質(zhì)量,可持續(xù)開發(fā)不僅在于項(xiàng)目架構(gòu)設(shè)計(jì),還與代碼質(zhì)量密切相關(guān),代碼的整潔度和質(zhì)量成正比,一份整潔的代碼在質(zhì)量上是可靠的,為團(tuán)隊(duì)開發(fā),后期維護(hù),重構(gòu)奠定了良好的基礎(chǔ)。
現(xiàn)在的軟件系統(tǒng)開發(fā)難度主要在于其復(fù)雜度和規(guī)模,客戶需求也不再像Winston Royce瀑布模型期望那樣在系統(tǒng)編碼前完成所有的設(shè)計(jì)滿足用戶軟件需求。在這個(gè)信息爆炸技術(shù)日新月異的時(shí)代,需求總是在不停的變化,隨之在2001年業(yè)界17位大牛聚集在美國(guó)猶他州的滑雪勝地雪鳥(Snowbird)雪場(chǎng),提出了“Agile”(敏捷)軟件開發(fā)價(jià)值觀,并在他們的努力推動(dòng)下,開始在業(yè)界流行起來。在《代碼整潔之道》(Clean Code),提出一種軟件質(zhì)量,可持續(xù)開發(fā)不僅在于項(xiàng)目架構(gòu)設(shè)計(jì),還與代碼質(zhì)量密切相關(guān),代碼的整潔度和質(zhì)量成正比,一份整潔的代碼在質(zhì)量上是可靠的,為團(tuán)隊(duì)開發(fā),后期維護(hù),重構(gòu)奠定了良好的基礎(chǔ)。在這本書中作者提出了注重實(shí)際開發(fā)實(shí)踐的細(xì)節(jié),而不是站在空洞的理論來談?wù)撜麧嵵馈?/p>
什么是整潔代碼?不同的人會(huì)站在不同的角度闡述不同的說法。而我最喜歡的是Grady Booch(《面向?qū)ο蠓治雠c設(shè)計(jì)》作者)闡述:
“整潔的代碼簡(jiǎn)單直接。整潔的代碼如同優(yōu)美的散文。整潔的代碼從不隱藏設(shè)計(jì)者的意圖,充滿了干凈利落的抽象和直截了當(dāng)?shù)目刂普Z(yǔ)句。”
整潔的代碼就是一種簡(jiǎn)約(簡(jiǎn)單而不過于太簡(jiǎn)單)的設(shè)計(jì),閱讀代碼的人能很清晰的明白這里在干什么,而不是隱澀難懂,整潔的代碼讀起來讓人感覺到就像閱讀散文-藝術(shù)的沉淀,作者是精心在意締造出來。
一:命名
命名包括變量、函數(shù)、參數(shù),類等,一個(gè)好的命名能夠很好的表述其所承載的業(yè)務(wù),從命名上就已經(jīng)很好的答復(fù)了為什么存在,做了什么事,應(yīng)該怎么用等的大部分的問題,閱讀者看到它的時(shí)候不必去深究其實(shí)現(xiàn)細(xì)節(jié),一切都在命名上一目了然。一個(gè)好的命名必須是名副其實(shí),不存在歧義(雙關(guān)語(yǔ)或常見屬于沖突),直接了當(dāng)(否定語(yǔ)句或者誤導(dǎo)性命名)。
二:函數(shù):
從匯編/C時(shí)代開始的到現(xiàn)在函數(shù)一直都存在與我們開發(fā)中不可或缺的一部分,結(jié)構(gòu)化組織,重用.作為函數(shù)式語(yǔ)言的一等公民,所有程序的第一組代碼。
好的函數(shù)必須足夠的小,其次還是足夠的小。很容易想像閱讀上千行的代碼,是多么巨大的自我心理挑戰(zhàn),在實(shí)習(xí)的時(shí)候工作于毫無(wú)分層邏輯的WinForm平臺(tái)下,完全依賴RAD模式帶來后置cs頁(yè)面上千行的代碼,每次修改都令我惱怒,恨不得重寫整個(gè)業(yè)務(wù)邏輯。
一個(gè)函數(shù)在于短小精悍,只作一件事情,并做好這件事,只做一件事才能得到更好的利用函數(shù)名表述自己。
好的函數(shù)還應(yīng)該是CQS(查詢命令分離)無(wú)副作用的(不存在隱藏歧義的背后邏輯),并對(duì)其他類型不存在“依戀情節(jié)(Feature Envy)“(類中的變量被所有的函數(shù)使用這是理想的高內(nèi)聚,萬(wàn)物皆有其位,而后物盡歸其位)。
函數(shù)的參數(shù)應(yīng)該足夠的少,無(wú)最好,一次之,再次為二,盡量避免三個(gè)以及三個(gè)以上,對(duì)于太多的參數(shù)你可能該采用IntroduceParameterObject(引入?yún)?shù)對(duì)象)。
重復(fù)的代碼。重復(fù)在軟件系統(tǒng)是萬(wàn)惡的,我們熟悉的分離關(guān)注點(diǎn),面向?qū)ο螅O(shè)計(jì)原則…都是為了減少重復(fù)提高重用,Don’t repeat yourself!(DRY)。
三:注釋、格式:
并不是寫出完備的注釋就是好的開發(fā)人員,如果代碼清晰的表述自己意圖,那么注釋反而多余。在《重構(gòu)-改善現(xiàn)有代碼之道》中Martin Fowler指出多余的注釋是一種代碼壞味道。就是好的注釋隨著項(xiàng)目的維護(hù)不斷的重構(gòu)很多時(shí)候也會(huì)變得不那么適應(yīng),而我們很少會(huì)去主動(dòng)維護(hù)。再則誤導(dǎo)性的注釋更為使用者所憎恨。當(dāng)然有時(shí)我們也得使用注釋,注釋并不是萬(wàn)惡的,當(dāng)我們沒法用代碼來描述自己的時(shí)候,我們需要注釋去描述意圖;多余有副作用的代碼給使用者提供警告注釋。TODO開發(fā)時(shí)進(jìn)度控制,比如你在進(jìn)行較大規(guī)模領(lǐng)域重構(gòu),目前有些邏輯不再適應(yīng),不那么自然,而對(duì)它的重構(gòu)還在任務(wù)列表最后,你可以選擇標(biāo)注在TODO中,最后完成從ToDoList中去掉每一個(gè)TODO任務(wù)。
良好的代碼格式,會(huì)使得我們閱讀更容易,一套共同的格式會(huì)讓我們查找理解更快速。每個(gè)團(tuán)隊(duì)都應(yīng)該遵循一套固定的代碼格式規(guī)范,整個(gè)軟件系統(tǒng)的統(tǒng)風(fēng)格統(tǒng)一,而不是各自為政各成一體。
四:對(duì)象和數(shù)據(jù)結(jié)構(gòu):
數(shù)據(jù)結(jié)構(gòu)指的就是數(shù)據(jù)的載體,暴露數(shù)據(jù),而幾乎沒有有意義的行為的貧血類。最常見的應(yīng)用在分布式服務(wù),以wcf,webservice,reset之類的分布式服務(wù)中不可或缺的數(shù)據(jù)傳輸對(duì)象(DTO)模式,DTO(Request/Response)就是一個(gè)很典型的數(shù)據(jù)載體,只存在簡(jiǎn)單的get,set屬性,并且更傾向于作為值對(duì)象存在。而對(duì)象則剛好相反作為面向?qū)ο蟮漠a(chǎn)物,必須封裝隱藏?cái)?shù)據(jù),而暴露出行為接口,DDD中領(lǐng)域模型傾向于對(duì)象不僅在數(shù)據(jù)更多暴露行為操作自己或者關(guān)聯(lián)狀態(tài)。
數(shù)據(jù)結(jié)構(gòu)和對(duì)象之間看是細(xì)微的差別卻導(dǎo)致了不同的本質(zhì)區(qū)別:使用數(shù)據(jù)結(jié)構(gòu)的代碼便于在不改動(dòng)現(xiàn)在數(shù)據(jù)結(jié)構(gòu)的前提下添加新的行為(函數(shù)),面向?qū)ο蟠a則便于不改動(dòng)現(xiàn) 有函數(shù)的前提下添加新的類。換句話說就是數(shù)據(jù)結(jié)構(gòu)難以添加新的的數(shù)據(jù)類型,因?yàn)樾枰膭?dòng)所有函數(shù),面向?qū)ο蟮拇a則難以添加新的函數(shù),因?yàn)樾枰薷乃械念悺T谌魏我粋€(gè)復(fù)雜的系統(tǒng)都會(huì)同時(shí)存在數(shù)據(jù)結(jié)構(gòu)和對(duì)象,我們需要判斷的是我們需要的是需要添加的新的數(shù)據(jù)類型還是新的行為函數(shù)。
隱藏作為面向?qū)ο笾饕匦灾械淖钪匾匦裕庋b隱藏是面向?qū)ο笾凶钪匾奶匦裕粋€(gè)好的面向?qū)ο蟠a肯定是對(duì)對(duì)象的內(nèi)部細(xì)節(jié)做到很好的隱藏封裝,封裝過后才有是多態(tài),委派之類的。一個(gè)好的面向?qū)ο蟮拇a一定是具有很好的隱藏封裝,易于測(cè)試,不穩(wěn)定因素往往集中在一處很小或者固定的位置,不穩(wěn)定因素的變更不會(huì)導(dǎo)致更大面積的修改擴(kuò)散。
對(duì)象的隱藏要求:方法不應(yīng)和任何調(diào)用方法返回的對(duì)象操作,換句話之和朋友說話,不和陌生人說話(迪米特法則,或被譯為最小知識(shí)原則),比如:ctxt.getOptions().getSearchDir().getAbsolutePath(),就是迪米特法則的反例模式。
五:異常處理:
每個(gè)軟件系統(tǒng)都避不開異常處理,需要防止它搞亂我們的邏輯。
利用異常處理代替返回異常編碼,返回異常編碼會(huì)是的代碼中充滿了if/else,switch/case擾亂我的代碼流轉(zhuǎn)。
對(duì)于特定異常撲捉,可以面向異常編程,編寫特定的異常類,使得對(duì)異常封裝轉(zhuǎn)化,更容易捕善后獲處理。
避免返回null,在軟件系統(tǒng)中最常見頭疼的就是NullReferenceException。在非特定場(chǎng)景下,我們應(yīng)該極力的避免返回null。面對(duì)這種場(chǎng)景我們可以采用null object Pattern(空對(duì)象模式)返回特例對(duì)象,如c#類庫(kù)中的Guid.Empty,string.Empty;對(duì)于集合類型我們可以返回長(zhǎng)度0的空集合而非null;
六:邊界:
在系統(tǒng)開發(fā)中不可能一切都得從零開始,自己寫所有的代碼,更好的方案是需要整合一些開源或者第三方的項(xiàng)目,為我所用。但是不能讓這些非自己的代碼滲侵中我們的代碼各處,有一些所以功能很強(qiáng)大的第三方產(chǎn)品,但不一定具有很好的抽象。很多時(shí)候我更寧愿花些時(shí)間抽象出我們自己所需要的接口在第三方類庫(kù)上外覆一層自己的抽象,這樣不僅便于TDD,因?yàn)槲覀兡軌蚝芎玫膭?chuàng)建偽對(duì)象,使的測(cè)試獨(dú)立不依賴外部資源,得到快速反饋;而且在設(shè)計(jì)上得到很好的擴(kuò)展,當(dāng)由于某些原因如第三方類庫(kù)不再能滿足業(yè)務(wù)需求,或者權(quán)益收費(fèi)等等,我們可以很好的切換底層而使得修改不會(huì)擴(kuò)散到系統(tǒng)各處。外覆類也是處理遺留代碼帶入測(cè)試容器的一種很好實(shí)踐。
七:單元測(cè)試:
TDD中測(cè)試代碼在往往和產(chǎn)品代碼差不多,在系統(tǒng)中占據(jù)一半的代碼量,不好的測(cè)試代碼也可能拖累項(xiàng)目的開發(fā)。整潔的測(cè)試代碼應(yīng)該是遵循first原則的:
快速(Fast):測(cè)試應(yīng)該快速,因?yàn)樾枰粩嗟倪\(yùn)行測(cè)試得到反饋,我們需要的快速反饋,錯(cuò)誤的快速定位。所以你的測(cè)試就不能依賴太多的外部資源,數(shù)據(jù)庫(kù),硬件環(huán)境等等,對(duì)于這些外部資源應(yīng)該采用偽對(duì)象模式來隔離。
獨(dú)立(Independent):測(cè)試應(yīng)該是獨(dú)立的,獨(dú)立于測(cè)試用例之間,獨(dú)立于特定的環(huán)境,獨(dú)立于測(cè)試的運(yùn)行順利。數(shù)據(jù)的獨(dú)立通常采用兩種獨(dú)立方式,每個(gè)測(cè)試環(huán)境的獨(dú)立,很多時(shí)候我們希望每個(gè)測(cè)試運(yùn)行完成后環(huán)境(如數(shù)據(jù)庫(kù))和運(yùn)行前保持一致,如數(shù)據(jù)庫(kù)高層次測(cè)試我們更希望在每次測(cè)試完成后不會(huì)帶來多余或者改變數(shù)據(jù)。再則就是數(shù)據(jù)的隔離,我們的行為測(cè)試(BDD,集成高角度的測(cè)試)都會(huì)依賴一些固定的信息,通常是登陸系統(tǒng)的人員,我們可以采用么個(gè)測(cè)試建立一個(gè)不同的登陸人員來使的每個(gè)測(cè)試之間的s數(shù)據(jù)隔離。
可重復(fù)(Repeatable):測(cè)試應(yīng)該可以在任何環(huán)境下可重復(fù),可運(yùn)行,因?yàn)闇y(cè)試獨(dú)立于環(huán)境外部資源。
自足驗(yàn)證(Self-Validation):測(cè)試應(yīng)該有通過失敗的標(biāo)示,從每一個(gè)測(cè)試上能得到一處代碼邏輯的通過失敗。每個(gè)測(cè)試都有對(duì)同一件事物的一種行為的斷言,也之?dāng)嘌砸患拢瑥亩軌蚝芎玫腻e(cuò)誤定位,避免高技巧性的測(cè)試。
及時(shí)(Timely):測(cè)試應(yīng)該是及時(shí)編寫的,TDD要求測(cè)試必須在實(shí)現(xiàn)代碼之前,提前以使用者的角度定義使用接口方式。如果你是在編碼后補(bǔ)測(cè)試,你的測(cè)試覆蓋很可能不夠,而且容易定式于實(shí)現(xiàn)的邏輯寫測(cè)試,很多時(shí)候?qū)τ谳^低層次的測(cè)試也不是那么容易寫的。一個(gè)設(shè)計(jì)良好的代碼必須也是可測(cè)試的。
八:類:
面向?qū)ο蟮南嗨菩袨榈某橄螅瘮?shù)代碼塊的組織形式,在面向?qū)ο笾形覀兊能浖到y(tǒng)是由眾多的類和類之間的交互協(xié)作完成了。面向?qū)ο筇卣鳎悍庋b,繼承,多態(tài)度,委派。一個(gè)設(shè)計(jì)良好的類該是具有良好的封裝,站在使用者的調(diào)度考慮那些是使用接口,那些是內(nèi)部細(xì)節(jié);這是面向?qū)ο笞钪饕奶卣鳎怯袝r(shí)會(huì)與測(cè)試沖突,可以適當(dāng)?shù)姆砰_并僅限于于測(cè)試調(diào)用。繼承和多態(tài)在面向?qū)ο笾锌梢詫?shí)現(xiàn)重用,但我更傾向于繼承不是為了重用,而是隔離變化;大量的濫用繼承不干凈的繼承體系將會(huì)導(dǎo)致龐大的繼承體系,繼承體系中眾多職責(zé)重復(fù)在各個(gè)同級(jí)派生類,理想的繼承應(yīng)該是滿足里氏替換原則(LSP:每個(gè)父類出現(xiàn)的地方都應(yīng)該可以被派生類所替換,并且能正確的工作);面oo第二原則組合優(yōu)先。而委派則是一個(gè)類把部分功能委派給其他類來完成,體現(xiàn)類之間的協(xié)作,類似組合。
類第一原則應(yīng)是是小并足夠的小。但與函數(shù)不同的是函數(shù)以代碼行數(shù)統(tǒng)計(jì),而類以權(quán)責(zé)統(tǒng)計(jì)。
單一原則(SRP),體現(xiàn)了類只應(yīng)該做一件事,并且做好它,這樣變化修改的理由只有他所做的事。良好的軟件設(shè)計(jì)中系統(tǒng)是由一組大量的短小的類和他們之間功能協(xié)作完成的,而不是幾個(gè)上帝類。
內(nèi)聚:高內(nèi)聚低耦合:提出與結(jié)構(gòu)化編程,內(nèi)聚表述模塊內(nèi)部功能不同操作邏輯之間的距離,如果一個(gè)類的每個(gè)變量都被每個(gè)方法所使用為最大的內(nèi)聚;耦合描述模塊之間的依賴程度;高內(nèi)聚低耦合以簡(jiǎn)單的方式表述就是功能完備(高內(nèi)聚)對(duì)象之間是通過穩(wěn)定的接口(低耦合)交互的。
依賴倒置(DIP):描述組件之間高層組件不應(yīng)該依賴于底層組件。依賴倒置是指實(shí)現(xiàn)和接口倒置,采用自頂向下的方式關(guān)注所需的底層組件接口,而不是其實(shí)現(xiàn)。DI模式很好的就是應(yīng)用IOC(控制反轉(zhuǎn))框架,構(gòu)造方式分為分構(gòu)造注入,函數(shù)注入,屬性注入;.net平臺(tái)流行的IOC框架有Unity,Castle windsor,Ninject,Autofac等框架支持,
九:并發(fā)編程:
并發(fā)是一種時(shí)間(When)和目的(What)的解耦,提供應(yīng)用程序的吞吐量,提高cpu利用率;但是并發(fā)編碼不是那么容易,再加上臨界資源競(jìng)爭(zhēng)死鎖。在并發(fā)編程的時(shí)候我們必須盡量遵守一些原則:
并發(fā)已經(jīng)足夠復(fù)雜,我們更需要代碼分離,分離線程相關(guān)代碼和非線程相關(guān)代碼,單一權(quán)責(zé),盡可能降低其復(fù)雜度。
限制臨街資源的作用域,為臨界資源加鎖是防止并發(fā)的策略,但是必須正確的加鎖,如果形成等待環(huán),就導(dǎo)致死鎖。
利用數(shù)據(jù)副本(值對(duì)象或者克隆)在線程之間傳遞數(shù)據(jù),避免線程之前操作的并發(fā)影響;線程獨(dú)立,使其在自己的環(huán)境中運(yùn)行,不能其他線程共享數(shù)據(jù)。
對(duì)于臨界資源加鎖應(yīng)盡量保持加鎖范圍盡可能的小。
更多關(guān)于簡(jiǎn)單設(shè)計(jì),迭進(jìn),逐步編程代碼,壞味道,并發(fā)示例請(qǐng)參見代碼整潔之道。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/64119.html
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。取這樣的名字不是因?yàn)橹旒沂歉銛?shù)學(xué)的,而是因?yàn)樵谠习傩杖绻荒苌蠈W(xué)和當(dāng)官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學(xué),更是一種藝術(shù)。 在小朱元璋出生一個(gè)月后,父母為他取了一個(gè)名字(元時(shí)慣例):朱重八,這個(gè)名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。朱重八高祖名字:朱百六;朱...
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。取這樣的名字不是因?yàn)橹旒沂歉銛?shù)學(xué)的,而是因?yàn)樵谠习傩杖绻荒苌蠈W(xué)和當(dāng)官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學(xué),更是一種藝術(shù)。 在小朱元璋出生一個(gè)月后,父母為他取了一個(gè)名字(元時(shí)慣例):朱重八,這個(gè)名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。朱重八高祖名字:朱百六;朱...
摘要:每個(gè)軟件系統(tǒng)都提供兩個(gè)價(jià)值給利益相關(guān)者表現(xiàn)和結(jié)構(gòu)。來自利益相關(guān)者的觀點(diǎn),開發(fā)者僅僅只提供了一些形態(tài)上的粗略改變,來自開發(fā)者的觀點(diǎn),老板的需求越來越難。記住,作為一個(gè)開發(fā)者,你就是利益相關(guān)者,你需要維護(hù)的軟件里有你的利益。 每個(gè)軟件系統(tǒng)都提供兩個(gè)價(jià)值給利益相關(guān)者:表現(xiàn)和結(jié)構(gòu)。軟件開發(fā)者應(yīng)的確保這兩個(gè)價(jià)值盡量高負(fù)責(zé)。然而很不幸,程序員很多只關(guān)心其中一個(gè)而忽略另一個(gè),甚至更不幸,他們可能關(guān)注...
閱讀 2900·2021-11-23 09:51
閱讀 1547·2021-11-15 11:36
閱讀 3006·2021-10-13 09:40
閱讀 1863·2021-09-28 09:35
閱讀 13039·2021-09-22 15:00
閱讀 1367·2019-08-29 13:56
閱讀 2923·2019-08-29 13:04
閱讀 2697·2019-08-28 18:06