如何高效編寫可維護代碼?

參見原文:如何高效編寫可維護代碼?函數

在代碼中找到一個放錯地方而且沒有用的註釋是否是頗有趣呢?
怎麼樣才能作到寫不多的註釋但仍能讓代碼易於理解呢?
一個主要的方式就是讓代碼自我文檔化。當代碼自我文檔化的時候,就不須要註釋去它的做用或者目的,而且也能使代碼變得很是容易維護。this

在這篇文章中,我將提供一些讓你的代碼自我文檔化的方式。下面就是三種使得代碼自文檔化的基本方法:spa

  • 命名:利用名字來解釋變量、函數等的目的。
  • 封裝函數:將一些特定功能的代碼封裝成一個函數以明確目的。
  • 引入變量:將表達式插入至專用變量。

這可能看上去很簡單,但在實際操做過程當中會讓人以爲有點棘手。首先你得明白哪些地方有問題以及哪些地方適用這些方法。code

此外,除了上述三種,還有一些應用比較普遍的方式:對象

  • 類和模塊接口:將類和模塊中的函數暴露出來,讓代碼更加清晰。
  • 代碼分組:用組來區分不一樣的代碼片斷。

接下來咱們將經過實例,具體講一講如何在實際應用中運用上述5個方法。接口

命名

首先,看幾個如何利用命名時代碼變得清晰和自我文檔化的例子。
1) 重命名函數
給函數命名不是很難,你能夠遵照如下規則:ip

  • 避免使用含糊的字眼,例如「handle」或「manage」——handleLinks、manageObjects。
  • 使用主動動詞——cutGrass、sendFile,以表示函數主動執行。
  • 指定返回值類型——getMagicBullet、READFILE。強類型的語言也能夠用類型標識符來代表函數的返回值類型。

2) 重命名變量文檔

  • 指定單位——若是裏面有數值參數,那能夠加上其單位。例如,用widthPx來取代width以指定寬度的單位是像素。
  • 不要使用快捷鍵——a和b都不能做爲參數名。

函數封裝

接下來,看幾個如何將代碼封裝成函數的例子。封裝函數的一個好處就是避免代碼重複,或者說改進代碼結構。get

1) 將代碼封裝成函數
這是最基本的:將代碼封裝成函數以明確其目的。猜猜下面這行代碼是幹什麼的:it

1
var width = (value - 0.5) * 16;

好像不是很清楚,固然有註釋就一清二楚了,可是咱們徹底能夠封裝成函數以實現自文檔化……

1
2
3
4
5
var width = emToPixels(value);
function emToPixels(ems) {
return (ems - 0.5) * 16;
}

惟一改變的是計算過程被轉移到了一個函數裏。該函數名明確地表達了它要作什麼,這樣一來就沒必要寫註釋了。並且,若是有須要後面還能夠直接調用此函數,一箭雙鵰,減小了重複勞動。

2) 用函數代替條件表達式
If語句若是包含多個運算對象,不寫註釋的話理解起來就比較難

1
2
if(!el.offsetWidth || !el.offsetHeight) {
}

知道上面這代碼的目的不?

1
2
3
4
5
6
function isVisible(el) {
return el.offsetWidth && el.offsetHeight;
}
if(!isVisible(el)) {
}

引入變量

最後再講講如何引入變量。相較於上面兩個方法,這個可能沒那麼有用,可是不管如何,知道比不知道好
1) 用變量代替表達式
看看上面的例子

1
2
if(!el.offsetWidth || !el.offsetHeight) {
}

這下不封裝成函數,用引入變量代替

1
2
3
var isVisible = el.offsetWidth && el.offsetHeight;
if(!isVisible) {
}

2) 用變量代替方程式
咱們也能夠用來清楚說明覆雜程式:

1
return a * b + (c / d);

用變量來代替

1
2
3
var divisor = c / d;
var multiplier = a * b;
return multiplier + divisor;

類和模塊接口

類和模塊的接口——也是面向公共的方法和屬性——有點像說明如何使用的文檔。
看下面的例子:

1
2
3
4
5
6
7
8
class Box {
public  function setState(state) {
this.state = state;
}
public  function getState() {
return this.state;
}

這個類也能夠包含其餘代碼。我特地舉這個例子是想說明公共接口如何自文檔化。
你能說出這個類是如何被調用的嗎?很顯然,這並不明顯。
這兩個函數都應該換個合理的名字以表述它們的目的。但即使作到這一點,咱們仍是不怎麼清楚如何使用。而後就須要閱讀更多的代碼或者翻閱文檔。
可是若是咱們這樣改一下呢……

1
2
3
4
5
6
7
8
9
10
11
12
13
class Box {
public  function open() {
this.state = open;
}
public  function close() {
this.state = closed;
}
public  function isOpen() {
return this.state == open;
}
}

是否是清晰多了?注意:咱們只是改動了公共接口,其內部表達與原先的this.state狀態相同。

代碼分組

用組來區分不一樣的代碼片斷也是自文檔化的一種形式。例如,像這篇文章中說的那樣,咱們應該儘量將變量定義在靠近使用它的地方,而且儘量將變量分門別類。這也能夠用來指定不一樣代碼組之間的關係,這樣更加方便其餘人知道他們還須要瞭解哪些代碼組。

看下面的例子:

1
2
3
4
5
6
7
8
var foo = 1;
blah()
xyz();
bar(foo);
baz( 1337);
quux(foo);

與下面的比較:

1
2
3
4
5
6
7
8
var foo = 1;
bar(foo);
quux(foo);
blah()
xyz();
baz( 1337);

將foo的全部使用組合放在一塊兒,一眼望去就能知道各類關係。可是有時候咱們不得不在中間調用一些其餘函數。因此若是能夠那就儘可能使用代碼分組,若是不能夠,那就不要強求。

其它建議

  • 不要使用奇怪的標記,下面兩個是等價的
1
2
3
4
5
imTricky && doMagic();
if(imTricky) {
doMagic();
}

顯而後者比較好。語法技巧並無帶來什麼好處。

    • 命名常量:若是代碼裏面有一些特殊值,那最好給它們命名。var PURPOSE_OF_LIFE = 42;
    • 制定規則:最好遵循相同的命名規則。這樣閱讀的人就能在參考其餘代碼的基礎上正確猜想出各類事物的含義。
相關文章
相關標籤/搜索