成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

《代碼整潔之道》讀書筆記

liangzai_cool / 3576人閱讀

摘要:看完代碼整潔之道之后我受益匪淺,但等到自己實踐時卻很難按照書中給的建議編寫出整潔的代碼。意味著新人除了了解代碼邏輯之外,還需要學習這種編碼語言。代碼在演化,注釋卻不總是隨之變動。區(qū)隔與靠近空格強調左右兩邊的分割。

看完《代碼整潔之道》之后我受益匪淺,但等到自己實踐時卻很難按照書中給的建議編寫出整潔的代碼。一方面是規(guī)則太多,記不住,另一方面書上引用了大量示例代碼對這些規(guī)則進行佐證,在我記不住時亦不方便查閱。于是我把書中的規(guī)則摘了出來并加以一定的解釋,閑暇時候多過幾遍,希望這些規(guī)則時刻警示我,成為我的習慣。

想看此書卻還沒開始的人也可以從這篇筆記出發(fā),對筆記中列出的規(guī)則有疑問再翻書找答案,相信會比直接啃書來的快一些。

ps: 未必要嚴格遵循書中的規(guī)則,代碼不是八股文。

命名 1.避免誤導

"一組賬號"別用accountList表示,List對程序員有特殊含義,可以用accountGroupbunchOfAccounts、甚至是accounts

不使用區(qū)別較小的名稱,ZYXControllerForEfficientHandlingOfStrings和ZYXControllerForEfficientStorageOfStrings難以辨別

不使用小寫l、大寫O作變量名,看起來像常量1、0

2.做有意義的區(qū)分

不以數(shù)字系列命名(a1、a2、a3),按照真實含義命名

Product/ProductInfo/ProductData 意思無區(qū)別,只統(tǒng)一用一個

別寫冗余的名字,變量名別帶variable、表名別帶table

3.使用可搜索的名稱

單字母名稱和數(shù)字常量很難在上下文中找出。名稱長短應與其作用域大小相對應,越是頻繁出現(xiàn)的變量名稱得越容易搜索(越長)

4.命名時避免使用編碼

把類型和作用域編碼進名稱里增加了解碼負擔。意味著新人除了了解代碼邏輯之外,還需要學習這種編碼語言。

別使用匈牙利語標記法(格式:[Prefix]-BaseTag-Name 其中BaseTag是數(shù)據(jù)類型的縮寫,Name是變量名字),純屬多余

不必用"m_"前綴來表明成員變量

接口和實現(xiàn)別在名稱中編碼。接口名IShapeFactory前導"I"是廢話。如果接口和實現(xiàn)必須選一個編碼,寧可選實現(xiàn),ShapeFactoryImp都比對接口名稱編碼來的好

5.類名、方法名

類名應當是名詞或名詞短語,方法名應當是動詞或動詞短語

6.每個概念用一個詞

fetch、retrieve、get約定一個一直用即可

7.別用雙關語

add方法一般語義是:根據(jù)兩個值獲得一個新的值。如果要把單個值加入到某個集合,用insertappend命名更好,這里用add就是雙關語了。

8.添加有意義的語境

很少有名稱能自我說明,需要用良好命名的類、函數(shù)、或者命名空間來放置名稱,給讀者提供語境,如果做不到的話,給名稱添加前綴就是最后一招了。

函數(shù) 1.越短小越好

if/else/while語句的代碼塊應該只有一行,該行應該是一個函數(shù)調用語句。

函數(shù)的縮進層級不應該多于一層或兩層。

2.只做一件事

如果函數(shù)只是做了該函數(shù)名下同一抽象層上的步驟,則函數(shù)只做了一件事。

要判斷函數(shù)是否不止做了一件事,就是要看是否能再拆出一個函數(shù)。

3.每個函數(shù)一個抽象層級 4.switch語句

把switch埋在較低的抽象層級,一般可以放在抽象工廠底下,用于創(chuàng)建多態(tài)對象。

5.使用描述性的名稱

函數(shù)越短小、功能越集中,就越便于取個好名字。

別害怕長名稱,長而具有描述性的名稱,要比短而令人費解的名稱好,要比描述性的長注釋好。

別害怕花時間取名字。

6.函數(shù)參數(shù)

參數(shù)越少越好,0參數(shù)最好,盡量避免用三個以上參數(shù)

參數(shù)越多,編寫組合參數(shù)的測試用例就越困難

別用標識參數(shù),向函數(shù)傳入bool值是不好的,這意味著函數(shù)不止做一件事??梢詫⒋撕瘮?shù)拆成兩個。

如果函數(shù)需要兩個、三個或者三個以上參數(shù),就說明其中一些參數(shù)應該封裝成類了。

將參數(shù)的順序編碼進函數(shù)名,減輕記憶參數(shù)順序的負擔,例如,assertExpectedEqualsActual(expected, actual)

7.副作用(函數(shù)在正常工作任務之外對外部環(huán)境所施加的影響)

檢查密碼并且初始化session的方法 命名為checkPasswordAndInitializeSession而非checkPassword,即使違反單一職責原則也不要有副作用

避免使用"輸出參數(shù)",如果函數(shù)必須修改某種狀態(tài),就修改所屬對象的狀態(tài)吧

8.設置(寫)和查詢(讀)分離

if(set("username", "unclebob")) { ... } 的含義模糊不清。應該改為:

if (attributeExists("username")) { 
    setAttribute("username", "unclebob");
    ...
}

9.使用異常代替返回錯誤碼

返回錯誤碼會要求調用者立刻處理錯誤,從而引起深層次的嵌套結構:

if (deletePate(page) == E_OK) {
    if (xxx() == E_OK) {
        if (yyy() == E_OK) {
            log();
        } else {
            log();
        }
    } else {
        log();
    }
} else {
    log();
}

使用異常機制:

try {
    deletePage();
    xxx();
    yyy();
} catch (Exception e) {
    log(e->getMessage());
}

try/catch代碼塊丑陋不堪,所以最好把try和catch代碼塊的主體抽離出來,多帶帶形成函數(shù)

try {
    do();
} catch (Exception e) {
    handle();
}

函數(shù)只做一件事,錯誤處理就是一件事。如果關鍵字try在某個函數(shù)中存在,它就該是函數(shù)的第一個單詞,而且catch代碼塊后面也不該有其他內(nèi)容。

定義錯誤碼的class需要添加新的錯誤碼時,所有用到錯誤碼class的其他class都得重新編譯和部署。但如果使用異常而非錯誤碼,新異??梢詮漠惓lass派生出來,無需重新編譯或部署。這也是開放閉合原則(對擴展開放,對修改封閉)的范例。

10.不要寫重復代碼

重復是軟件中一切邪惡的根源。當算法改變時需要修改多處地方

11.結構化編程

只要函數(shù)保持短小,偶爾出現(xiàn)的return、break、continue語句沒有壞處,甚至還比單入單出原則更具有表達力。goto只有在大函數(shù)里才有道理,應該盡量避免使用。

12.如何寫出這樣的函數(shù)

并不需要一開始就按照這些規(guī)則寫函數(shù),沒人做得到。想些什么就寫什么,然后再打磨這些代碼,按照這些規(guī)則組裝函數(shù)。

注釋

若編程語言足夠有表現(xiàn)力,我們就不需要注釋。

注釋總是一種失敗。

代碼在演化,注釋卻不總是隨之變動。

不準確的注釋比沒注釋壞的多。

1.用代碼來闡述

創(chuàng)建一個與注釋所言同一事物的函數(shù)即可

// check to see if the employee is eligible for full benefits
if ((employee.falgs & HOURLY_FLAG) && (employee.age > 65))

應替換為

if (employee.isEligibleForFullBenefits())

2.好注釋

法律信息

提供基本信息,如解釋某個抽象方法的返回值

對意圖的解釋,反應了作者某個決定后面的意圖

闡釋。把某些晦澀的參數(shù)或者返回值的意義翻譯成可讀的形式(更好的方法是讓它們自身變得足夠清晰,但是類似標準庫的代碼我們無法修改):

if (b.compareTo(a) == 1) //b > a

警示。// don"t run unless you have some time to kill

TODO注釋

放大 一些看似不合理之物 的重要性

3.壞注釋

自言自語

多余的注釋。把邏輯在注釋里寫一遍不能比代碼提供更多信息,讀它不比讀代碼簡單。一目了然的成員變量別加注釋,顯得很多余。

誤導性注釋

遵循規(guī)矩的注釋。每個函數(shù)都加注釋、每個變量都加注釋是愚蠢的。

日志式注釋。有了代碼版本控制工具,不必在文件開頭維護修改時間、修改人這類日志式的注釋

能用函數(shù)或者變量表示就別用注釋:

// does the module from the global list  
// depend on the subsystem we are part of?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem())

可以改為

ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

位置標記。標記多了會被我們忽略掉:
///////////////////// Actions //////////////////////////

右括號注釋

try {
    while () {
        if () {
            ...
        } // if
        ...
    } // while
    ...
} // try

如果你想標記右括號,其實應該做的是縮短函數(shù)

署名 /* add by rick */ 源代碼控制工具會記住你,署名注釋跟不上代碼的演變。

注釋掉的代碼。會導致看到這段代碼其他人不敢刪除

信息過多。別在注釋中添加有趣的歷史話題或者無關的細節(jié)

沒解釋清楚的注釋。注釋的作用是解釋未能自行解釋的代碼,如果注釋本身還需要解釋就太遺憾了。

短函數(shù)的函數(shù)頭注釋。為短函數(shù)選個好名字比函數(shù)頭注釋要好。

非公共API函數(shù)的javadoc/phpdoc注釋。

代碼格式 1.垂直格式

短文件比長文件更易于理解。平均200行,最多不超過500行的單個文件可以構造出色的系統(tǒng)

區(qū)隔: 封包聲明、導入聲明、每個函數(shù)之間,都用空白行分隔開,空白行下面標識著新的獨立概念

靠近: 緊密相關的代碼應該互相靠近,例如一個類里的屬性之間別用空白行隔開

變量聲明應盡可能靠近其使用位置:循環(huán)中的控制變量應該總是在循環(huán)語句中聲明。

成員變量應該放在類的頂部聲明,不要四處放置

如果某個函數(shù)調用了另外一個,就應該把它們放在一起。我們希望底層細節(jié)最后展現(xiàn)出來,不用沉溺于細節(jié),所以調用者盡可能放在被調用者之上。

執(zhí)行同一基礎任務的幾個函數(shù)應該放在一起。

2.水平格式

一行代碼不必死守80字符的上限,偶爾到達100字符不超過120字符即可。

區(qū)隔與靠近: 空格強調左右兩邊的分割。賦值運算符兩邊加空格,函數(shù)名與左圓括號之間不加空格,乘法運算符在與加減法運算符組合時不用加空格(a*b - c)

不必水平對齊。例如聲明一堆成員變量時,各行不用每一個單詞都對齊。

短小的if、while、函數(shù)里最好也不要違反縮進規(guī)則,不要這樣:
if (xx == yy) z = 1;

對象和數(shù)據(jù)結構

對象:暴露行為(接口),隱藏數(shù)據(jù)(私有變量)
數(shù)據(jù)結構:沒有明顯的行為(接口),暴露數(shù)據(jù)。如DTO(Data Transfer Objects)、Entity

1.對象與數(shù)據(jù)結構的反對稱性(參考書中代碼清單6-5)

使用數(shù)據(jù)結構便于在不改動現(xiàn)在數(shù)據(jù)結構的前提下添加新函數(shù);使用對象便于在不改動既有函數(shù)的前提下添加新類。

使用數(shù)據(jù)結構難以添加新數(shù)據(jù)結構,因為必須修改所有函數(shù);使用對象難以添加新函數(shù),因為必須修改所有類。

萬物皆對象只是個傳說,有時候我們也會在簡單數(shù)據(jù)結構上做一些過程式的操作。

2.Demeter定律(最少知識原則)

模塊不應該了解它所操作對象的內(nèi)部情形

class C的方法f只應該調用以下對象的方法:

C

在方法f里創(chuàng)建的對象

作為參數(shù)傳遞給方法f的對象

C持有的對象

方法不應調用 由任何函數(shù)返回的對象 的方法。下面的代碼違反了demeter定律:
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

一個簡單例子是,人可以命令一條狗行走(walk),但是不應該直接指揮狗的腿行走,應該由狗去指揮控制它的腿如何行走。

異常處理

異常處理很重要,但如果異常處理四處分散在代碼中 導致邏輯模糊不清,它就是錯的。

1.使用異常而不是返回錯誤碼

如果使用錯誤碼,調用者必須在函數(shù)返回時立刻處理錯誤,但這很容易被我們忘記。

錯誤碼通常會導致嵌套if else

2.先寫try-catch語句

當編寫可能會拋異常的代碼時,先寫好try-catch再往里堆邏輯

3.使用unchecked exception(java獨有) 4.在catch里盡可能的記錄錯誤信息,記錄失敗的操作以及失敗的類型。 5.根據(jù)調用者的需要 定義不同的異常處理類

同一個try里catch多個不同exception,但是catch處理的事(打日志等)是一致的,可以考慮用函數(shù)打包一下這個異常處理,針對不同異常僅需throw成同一個異常而不做任何處理,在外層catch時統(tǒng)一處理(打日志等)。如果僅想捕獲一部分異常而放過其他異常,就使用不同的函數(shù)打包這個異常處理。

6.特例模式: 創(chuàng)建一個類來處理特例。
try {
    MealExpendses expenses = expenseRepotDAO.getMeals(employee.getID());
    m_total += expenses.getTotal();// 如果消耗了餐食,計入總額
} catch (MealExpensesNotFound e) {
    m_total += getMealPeDiem();// 如果沒消耗,將員工補貼計入總額
}

異常打斷了業(yè)務邏輯??梢栽趃etMeals()里不拋異常,而是在沒消耗餐食的時候返回一個特殊的MealExpense對象(PerdiemMealExpense),復寫getTotal()方法。

MealExpendses expenses = expenseRepotDAO.getMeals(employee.getID());
m_total += expenses.getTotal();

publc class PerDiemMealExpenses implements MealExpenses {
    public int getTotal() {
        //return xxx; //返回員工補貼
    }
}
7.別返回null值

返回null值只要一處沒檢查null,應用程序就會失敗

當想返回null值的時候,可以試試拋出異常,或者返回特例模式的對象。

8.別傳遞null值

在方法中傳遞null值是一種糟糕的做法,應該盡量避免。

在方法里用if或assert過濾null值參數(shù),但是還是會出現(xiàn)運行時錯誤,沒有良好的辦法對付調動者意外傳入的null值,恰當?shù)淖龇ň褪?strong>禁止傳入null值。

邊界

將第三方代碼干凈利落地整合進自己的代碼中

1.避免公共API返回邊界接口,或者將邊界接口作為參數(shù)傳遞給API。將邊界保留在近親類中。 2.不要在生產(chǎn)代碼中試驗新東西,而是編寫測試來理解第三方代碼。 3.避免我們的代碼過多地了解第三方代碼中的特定信息。 單元測試 1.TDD(Test-driven development)三定律

First Law: You may not write production code until you have written a failing unit test.

Second Law: You may not write more of a unit test than is sufficient to fail, and not compiling is failing.

Third Law: You may not write more production code than is sufficient to pass the currently failing test.

2.保持測試整潔

臟測試等同于沒測試,測試代碼越臟 生產(chǎn)代碼越難修改。

測試代碼和生產(chǎn)代碼一樣重要。

整潔的測試代碼最應具有的要素是:整潔性。測試代碼中不要有大量重復代碼的調用。

3.每個測試一個斷言

每個測試函數(shù)有且僅有一個斷言語句。

每個測試函數(shù)中只測試一個概念。

4.整潔的測試依賴于FIRST規(guī)則

fast: 測試代碼應該能夠快速運行,因為我們需要頻繁運行它。

independent: 測試應該相互獨立,某個測試不應該依賴上一個測試的結果,測試可以以任何順序進行。

repeatable: 測試應可以在任何環(huán)境中通過

self-validating: 測試應該有bool值輸出,不應通過查看日志來確認測試結果,不應手工對比兩個文本文件確認測試結果。

timely: 及時編寫測試代碼。單元測試應該在生產(chǎn)代碼之前編寫,否則生產(chǎn)代碼會變得難以測試。

1.類的結構組織(順序):

公共靜態(tài)常量

私有靜態(tài)變量

私有實體變量

公共函數(shù)

私有工具函數(shù)

2.類應該短小

對于函數(shù)我們計算代碼行數(shù)衡量大小,對于類我們使用權責來衡量。

類的名稱應當描述其權責。類的命名是判斷類長度的第一個手段,如果無法為某個類命以準確的名稱,這個類就太長了。類名包含模糊的詞匯,如Processor、Manager、Super,這種現(xiàn)象就說明有不恰當?shù)?strong>*權責聚集情況。

單一權責原則: 類或者模塊應該有一個權責——只有一條修改的理由(A class should have only one reason to change.)。

系統(tǒng)應該由許多短小的類而不是少量巨大的類組成。

類應該只有少量的實體變量,如果一個類中每個實體變量都被每個方法所使用,則說明該類具有最大的內(nèi)聚性。創(chuàng)建最大化的內(nèi)聚類不太現(xiàn)實,但是應該以高內(nèi)聚為目標,內(nèi)聚性越高說明類中的方法和變量互相依賴、互相結合形成一個邏輯整體

保持內(nèi)聚性就會得到許多短小的類。如果你想把一個大函數(shù)的某一小部分拆解成多帶帶的函數(shù),拆解出的函數(shù)使用了大函數(shù)中的4個變量,不必將4個變量作為參數(shù)傳遞到新函數(shù)里,僅需將這4個變量提升為大函數(shù)所在類的實體變量,但是這么做卻因為實體變量的增多而喪失了類的內(nèi)聚性,更好多做法是讓這4個變量拆出來,擁有自己的類。將大函數(shù)拆解成小函數(shù)往往是將類拆分為小類的時機。

3.為修改而組織(參考書中代碼清單10-9、10-10)

類應當對擴展開放,對修改封閉(開放閉合原則)

在理想系統(tǒng)中,我們通過擴展系統(tǒng)而非修改現(xiàn)有代碼來添加新特性。

系統(tǒng) 1.將系統(tǒng)的構造與使用分開

將全部構造過程搬遷到main或者被稱之為main的模塊中,涉及系統(tǒng)其余部分時,假設所有對象都已經(jīng)正確構造。

有時應用程序也需要負責確定何時創(chuàng)建對象,我們可以使用抽象工廠模式讓應用自行控制何時創(chuàng)建對象,但構造能力在工廠實現(xiàn)類里,與使用部分分開。

依賴注入(DI),控制反轉(IoC)是分離構造與使用的強大機制。

emergent design

四條規(guī)矩幫助你創(chuàng)建優(yōu)良的設計

1.運行所有測試

緊耦合的代碼難以編寫測試,測試寫的越多,就越會遵循依賴注入之類的規(guī)則,使代碼加減少耦合。

測試消除了對清理代碼就會破壞代碼的恐懼。

2.消除重復

兩個方法提取共性到新方法中,新方法分解到另外的類里,從而提升其可見性。

模板方法模式是消除重復的通用技巧

// 原始邏輯
public class VacationPolicy() {
    public void accrueUSDivisionVacation() {
        //do x;
        //do US y;
        //do z;
    }
    public void accrueEUDivisionVacation() {
        //do x;
        //do EU y;
        //do z;
    }
}

// 模板方法模式重構之后
abstract public class VacationPolicy {
    public void accrueVacation() {
        x();
        y();
        z();
    }
    private void x() {
        //do x;
    }
    abstract protected void y() {
    
    }
    private void z() {
        //do z;
    }
}
public class USVacationPolicy extends VacationPolicy {
    protected void y() {
        //do US y;
    }
}
public class EUVacationPolicy extends VacationPolicy {
    protected void y() {
        //do EU y;
    }
}

3.表達意圖

作者把代碼寫的越清晰,其他人理解代碼就越快。

太多時候我們深入于要解決的問題中,寫出能工作的代碼之后,就轉移到下一個問題上,沒有下足功夫調整代碼讓后來者易于閱讀。多少尊重一下我們的手藝,花一點點時間在每個函數(shù)和類上。

4.盡可能少的類和方法

為了保持類和函數(shù)的短小,我們可能會早出太多細小的類和方法。

類和方法數(shù)量太多,有時是由毫無意義的教條主義導致的。

5.以上4條規(guī)則優(yōu)先級依次遞減。重要的是測試、消除重復、表達意圖。 并發(fā)編程 1.防御并發(fā)代碼問題的原則與技巧

遵循單一職責原則。分離并發(fā)代碼與非并發(fā)代碼

限制臨界區(qū)數(shù)量、限制對共享數(shù)據(jù)的訪問。

避免使用共享數(shù)據(jù),使用對象的副本。

線程盡可能地獨立,不與其他線程共享數(shù)據(jù)。

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://systransis.cn/yun/77889.html

相關文章

  • 代碼整潔之道讀書筆記

    摘要:但大多數(shù)情況下應該盡量利用一些機制將二元函數(shù)轉換成一元函數(shù)。應該為起一個更能描述函數(shù)功能的函數(shù)名副作用在于對這個調用函數(shù),顧名思義,就是用來檢查密碼。注釋及其描述的代碼之間的聯(lián)系應該顯而易見。受控異常的代價就是違反開放閉合原則。 大師級的程序員把系統(tǒng)當作故事來講,而不是當作程序來寫。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1fs0...

    pakolagij 評論0 收藏0
  • 代碼整潔之道

    摘要:在代碼整潔之道,提出一種軟件質量,可持續(xù)開發(fā)不僅在于項目架構設計,還與代碼質量密切相關,代碼的整潔度和質量成正比,一份整潔的代碼在質量上是可靠的,為團隊開發(fā),后期維護,重構奠定了良好的基礎。 現(xiàn)在的軟件系統(tǒng)開發(fā)難度主要在于其復雜度和規(guī)模,客戶需求也不再像Winston Royce瀑布模型期望那樣在系統(tǒng)編碼前完成所有的設計滿足用戶軟件需求。在這個信息爆炸技術日新月異的時代,需求總是在不停...

    icattlecoder 評論0 收藏0
  • 代碼整潔之道 - 有意義的命名

    摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點。取這樣的名字不是因為朱家是搞數(shù)學的,而是因為在元朝,老百姓如果不能上學和當官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學,更是一種藝術。 在小朱元璋出生一個月后,父母為他取了一個名字(元時慣例):朱重八,這個名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點。朱重八高祖名字:朱百六;朱...

    mengbo 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<