在過去幾天,我在文章中曾不只一次提到過邊界條件,第一次我只概略地說,我認為那是極端條件。雖然這個解釋不能算錯,然而隨著持續閱讀書中內容,我想稍微補充一下關於邊界條件的概念。
在程式功能的極盡之處,我們會需要多加一些條件判斷,好讓所撰寫的內容仍然可以正常運行,而這個條件,就稱之為邊界條件。例如分頁的判斷,就是一個很常遇到與撰寫的邊界條件。
const pagination = {
total: 10,
current: 1,
};
const next = () => {
if (pagination.current < pagination.total) {
pagination.current++;
}
};
上面是一個簡單的例子,當分頁到了最後一頁就無法再向後,是以我們會在當中加入判斷條件,避免程式出現錯誤。
在函式裡的敘述,都應該要撰寫在同樣的抽象層次上。這個層次應該比函式名稱所描述的抽象行為還低一個抽象層次。
函式的撰寫原則不難,但往往撰寫時仍會將不同層次的抽象概念混合,所以想補充、分享一個從混合到分離的例子,增加我們對於函式內容的敏感度。
public String render () throws Exception
{
StringBuffer html = new StringBuffer("<hr");
if(size > 0)
html.append(" size=\"").append(size + 1).append("\"");
html.append(">");
return html.toString();
}
這是一個來自 FitNesse 的範例。該函式會創建一個水平線標籤 ( hr ) ,並且這個水平線會隨著變數 size 去做粗細的調整。而這個函式至少包含兩個抽象概念,一個是 hr 標籤本身,另一個則是水平線的粗細。
我們可能會想讓程式碼的意圖與表達力更清晰一些,而對該函式進行重構:
public String render () throws Exception
{
HtmlTag hr = new HtmlTag("hr");
if(size > 0)
hr.addAttribute("size", "" + (size + 1));
return hr.html();
}
作者成功地將函式的抽象層次劃分開,可讀性也順利提升,但結果是,該函式仍然包含著不同層次的概念,所以再次對它進行調整:
public String render () throws Exception
{
HtmlTag hr = new HtmlTag("hr");
if(extraDashes > 0)
hr.addAttribute("size", hrSize(extraDashes));
return hr.html();
}
private String hrSize (int height)
{
int hrSize = height + 1;
return String.format("%d", hrSize);
}
兩個抽象概念被分別劃分至不同的函式中,保持函式遵循「只做一件事」的原則,並且將 size 真正的意圖凸顯出來 ──── 額外破折號的數量。至此順利完成了重構,將不同的概念分離開,使程式碼變得更加容易閱讀與理解。