
_注:正文中的引用是直接引用作者Bob大叔的話,兩條橫線中間的段落的是我自己的觀點(diǎn),其他大約都可以算是筆記了。 _
從這一章的第一段就能看出來(lái),Bob大叔對(duì)格式化是非??粗氐模B著使用了幾個(gè)排比句來(lái)說(shuō)明代碼的格式化對(duì)于一個(gè)工程作為一個(gè)整體的重要性。所有的代碼---不論是一個(gè)人不同時(shí)期寫(xiě)的代碼,還是一個(gè)團(tuán)隊(duì)不同的成員寫(xiě)的代碼---都應(yīng)該是一致的、優(yōu)雅的。
為什么要格式化
代碼的格式化直接影響到「使用代碼溝通」的問(wèn)題,而「使用代碼溝通」是一個(gè)專(zhuān)業(yè)的開(kāi)發(fā)者的第一要?jiǎng)?wù),而不是「只要能讓代碼能運(yùn)行起來(lái)就行」。
你今天寫(xiě)的功能可能不久之后就會(huì)需要被更改,但是代碼的「可讀性」將會(huì)對(duì)「未來(lái)代碼的修改或重構(gòu)」產(chǎn)生深遠(yuǎn)的影響。
接下來(lái)是一些對(duì)「使用代碼溝通」能力有影響的方面:
垂直方向上的格式化
垂直方向上的格式化簡(jiǎn)單說(shuō)就是代碼的長(zhǎng)度。
我們?cè)陂_(kāi)發(fā)的過(guò)程中應(yīng)該盡量去追求寫(xiě)出較小的代碼源文件,作者給出了一個(gè)參考值是:「大多數(shù)的文件應(yīng)該都小于200行,最長(zhǎng)的文件最好要小于500行」。但是作者也說(shuō)這條規(guī)則不應(yīng)該成為一個(gè)硬性的標(biāo)準(zhǔn),而是我們?cè)陂_(kāi)發(fā)過(guò)程中不斷追求的境界。畢竟有時(shí)我們不可避免地必須寫(xiě)出一些比較長(zhǎng)的源文件。
1. 報(bào)紙的比喻
一個(gè)源文件讀起來(lái)應(yīng)該像報(bào)紙里的文章一樣,名字應(yīng)該簡(jiǎn)短但能表達(dá)文章的基本意思,源文件剛開(kāi)始的部分應(yīng)該能夠提供一些高層的抽象和算法,然后具體的實(shí)現(xiàn)細(xì)節(jié)在接下來(lái)的章節(jié)中依次展開(kāi),直到源文件的最后,在那里我們能找到最低等級(jí)的函數(shù)和最細(xì)節(jié)的實(shí)現(xiàn)。
一張報(bào)紙(一個(gè)項(xiàng)目)會(huì)有很多文章(源文件)組成,大多數(shù)都很短,偶爾有一些稍微有些長(zhǎng),極少數(shù)的情況也會(huì)出現(xiàn)占據(jù)整個(gè)頁(yè)面的長(zhǎng)度的文章。
2. 不同概念之間的垂直間隔
在源文件中不同的概念(往往指的是函數(shù))之間應(yīng)該使用空行來(lái)隔開(kāi),這樣在讀者閱讀這段代碼時(shí)會(huì)非常舒服。
3. 垂直聚集
與上一條中闡述的內(nèi)容剛剛相反,相似或者相關(guān)性較強(qiáng)的概念之間往往不應(yīng)該有分隔(往往是注釋?zhuān)蛘呤菬o(wú)意義的空行),而應(yīng)該聚集在一起。
4. 垂直距離
我們經(jīng)常會(huì)碰到這種情況,我們閱讀代碼時(shí)迷失在了尋找「變量與變量間、函數(shù)與函數(shù)間、類(lèi)與類(lèi)之間的關(guān)系」的過(guò)程中不能自拔,大量的時(shí)間花在了尋找「那段代碼在哪里」。
相近的概念應(yīng)該在垂直距離上盡量接近,相似的概念應(yīng)該盡量存在于同一個(gè)函數(shù)(除非你有比較好的原因不去這么做),protected的變量應(yīng)該被禁止使用(這條可能在某些語(yǔ)言里不存在)。
變量的定義 應(yīng)該盡量靠近它被使用的地方。
實(shí)例變量(instance variable) (類(lèi)的動(dòng)態(tài)變量)應(yīng)該被定義在類(lèi)的起始。
不論是遵循「剪刀原則」,將實(shí)例變量定義在類(lèi)的最低端,還是根據(jù)Java的慣例,將實(shí)例變量定義在類(lèi)的最頂端,都是好的。但請(qǐng)不要把實(shí)例變量放在除了這兩種之外的其他位置。
有依賴關(guān)系的函數(shù) 應(yīng)該在垂直的距離上盡量靠近,而且往往調(diào)用者應(yīng)該在被調(diào)用者的上邊(如果可能的話)。
概念上關(guān)系較密切的代碼 應(yīng)該根據(jù)他們關(guān)系的密切程度來(lái)選擇他們間的距離
5. 垂直排序
垂直坐標(biāo)上的元素(可能是代碼塊,函數(shù),變量等)應(yīng)該按照自上而下的閱讀順序進(jìn)行排列,這樣更易于閱讀。
水平方向上的格式化
作者對(duì)于一些典型的應(yīng)用(Junit, FitNesse, testNG, Time and Money, JDepend, Ant, Tomcat)的行的寬度進(jìn)行了統(tǒng)計(jì),絕大多數(shù)的寬度都在60個(gè)字符以下,大于80個(gè)字符以上寬度的占比非常小。通常的說(shuō),代碼的行寬應(yīng)該小于100或120。
1. 水平的間隔和聚集
作者在這個(gè)小節(jié)里說(shuō)了一個(gè)比較微妙的事物---空格,因?yàn)橐褂每崭駚?lái)區(qū)別水平方向上的不同概念
//代碼4-1
private void measureLine(String line) {
lineCount++;
int lineSize = line.length();
totalChars += lineSize;
lineWidthHistogram.addLine(lineSize, lineCount); recordWidestLine(lineSize);
}
在水平方向上,作者主張?jiān)趶?qiáng)相關(guān)的元素之間不使用空格,而在弱相關(guān)的元素之間添加空格來(lái)予以區(qū)分,比如代碼4-1。
- 賦值語(yǔ)句的左右是兩個(gè)區(qū)別很大的部分,那么使用空格來(lái)突出賦值操作符(就是= )并對(duì)賦值語(yǔ)句的左右兩部分加以區(qū)分。
- 函數(shù)名和左括號(hào)之間是十分相關(guān)的,所以就不使用空格
- 參數(shù)之間的逗號(hào)分隔符之后使用空格,用以突出分隔符并區(qū)分兩個(gè)不同參數(shù)。
2. 水平對(duì)齊
有些人(包括Bob大叔自己早期)喜歡在有一堆的變量定義或賦值語(yǔ)句時(shí),使用對(duì)齊來(lái)使代碼變得更易讀,但這樣是沒(méi)有用的。使用這樣的對(duì)齊會(huì)喧賓奪主,把代碼閱讀者的閱讀重心吸引到了錯(cuò)誤的地方。
//代碼4-1
public class FitNesseExpediter implements ResponseSender
{
private Socket socket;
private InputStream input;
private OutputStream output;
private Request request;
private Response response;
private FitNesseContext context;
protected long requestParsingTimeLimit;
private long requestProgress;
private long requestParsingDeadline;
private boolean hasError;
public FitNesseExpediter(Socket s,
FitNesseContext context) throws Exception
{
this.context = context;
socket = s;
input = s.getInputStream();
output = s.getOutputStream();
requestParsingTimeLimit = 10000;
}
如果你的代碼中出現(xiàn)了很長(zhǎng)的變量定義或賦值語(yǔ)句列表,長(zhǎng)到需要使用左對(duì)齊來(lái)使它們更易讀,那么主要問(wèn)題應(yīng)該是你的代碼中不應(yīng)該出現(xiàn)這么長(zhǎng)的變量定義或賦值列表,而不是缺少了左對(duì)齊。
3. 縮進(jìn)
源文件里的內(nèi)容與其說(shuō)是「提綱和內(nèi)容」的關(guān)系,不如說(shuō)是「層級(jí)」關(guān)系。
在代碼中,要傳達(dá)的信息之間的從屬關(guān)系從文件到類(lèi)、類(lèi)到方法、方法到代碼塊、代碼塊到子代碼塊,逐級(jí)往下,為了區(qū)分這種層級(jí),開(kāi)發(fā)者非常喜歡使用代碼的縮進(jìn)來(lái)使代碼更加易讀。
縮進(jìn) 是代碼質(zhì)量管理里很重要的一環(huán),我個(gè)人比較喜歡使用制表符(TAB,往往是4個(gè)空格)來(lái)標(biāo)識(shí)縮進(jìn),然而我有個(gè)同學(xué)就特別喜歡使用2個(gè)空格來(lái)表示縮進(jìn),這種事情仁者見(jiàn)仁智者見(jiàn)智。
不要破壞縮進(jìn)(breaking indentation)。謹(jǐn)記一定要使用縮進(jìn)。
有時(shí)候一個(gè)if語(yǔ)句或者for循環(huán)代碼塊或者函數(shù)非常小,開(kāi)發(fā)者會(huì)覺(jué)得不需要使用縮進(jìn),而把大括號(hào),執(zhí)行語(yǔ)句等都寫(xiě)在同一行,如代碼4-4中所示,這種是必須要避免的。
//代碼4-3
public class CommentWidget extends TextWidget {
public static final String REGEXP = "^#[^\\r\\n]*(?:(?:\\r\\n)|\\n|\\r)?";
public CommentWidget(ParentWidget parent, String text){super(parent, text);}
public String render() throws Exception {return ""; }
}
4. 空語(yǔ)句
我個(gè)人非常不喜歡空語(yǔ)句,并且在開(kāi)發(fā)過(guò)程中是會(huì)盡力避免使用空語(yǔ)句的,如果我不得不使用它,也一定會(huì)加上大括號(hào)并且使用縮進(jìn)來(lái)讓代碼更易讀。
團(tuán)隊(duì)規(guī)則(Team Rules)
如果一個(gè)項(xiàng)目是一個(gè)團(tuán)隊(duì)協(xié)同開(kāi)發(fā),那么團(tuán)隊(duì)中的每個(gè)人都要遵循這個(gè)團(tuán)隊(duì)定下來(lái)的格式化要求,來(lái)保證一個(gè)項(xiàng)目中的格式是順滑的、一致的。