重構可以降低複雜度嗎?
是,也不是。
先從個例子說起。
前陣子在重構羅賓的 Side Project – 一個遊戲繪圖引擎 – 發現了一個應該要調整的問題。
攝影機 Frustum 的視野角度 (Field of View),羅賓是用原始的浮點數型態去做設定,像這樣:
class Frustum
{
public:
void fov(float value);
.....
};
當下的直覺,這個 value 的單位應該是弳度(Radian),而不是角度(Degree)。但是,也不是很肯定,所以就深入進實作程式碼去看看。
果然是弳度。
發現這個函式有更好的表達意圖的方式之後,羅賓便決定要來點小重構。
最簡單的方式是,把 value 改成 value_in_radian。但這其實並沒有改善多少,使用端還是會用浮點數型態來呼叫,”必須使用弳度”的知識與責任,仍然在呼叫端身上。
另一種方式是,建立兩個小小的值物件(Value Object),Radian與Degree。
class Radian
{
public:
Radian();
explicit Radian(float r);
Degree degree();
....
};
class Degree
{
public:
Degree();
explicit Degree(float d);
Radian radian();
....
};
兩個物件都很簡單,只有基本的算術運算,另外再加上彼此之間的轉換。
有了這兩個物件後,fov 的函式就可以改為
class Frustum
{
public:
void fov(const Radian& r);
....
};
於是乎,呼叫端不能再用原始的浮點數型態來呼叫,也不需要深入去問,該用弳度還是角度,函式本身已經表達很清楚了。
現在回到主題,重構能夠降低複雜度嗎?
從實作程式邏輯層面來看,不行。
一個系統行為的複雜度有多高,它的實作程式邏輯的複雜度就有多高。Martin Fowler (人稱: 馬丁·花)在重構的書裡說到:
重構是在不改變行為的前提下,所進行的修改與設計
看看上面的例子,在實作的程式碼上,羅賓不但沒有簡化它,還把它變得更複雜了,多了兩個物件,計算fov時還要取值…
所以重構並沒有簡化實作邏輯的複雜度。
但是如果拉高一點,從抽象概念的層次來看,這個重構是把複雜度降低了。
fov 與弳度的關係,現在清楚的表達了出來,不需要深入到實作的程式碼去探究,這部分已經被更好的封裝了起來。使用端所需要的知識,也不再需要牢牢記住浮點數的單位。
重構、設計模式、架構…這些東西,都不會讓我們的程式碼變簡單,系統行為有多複雜,程式碼就有多複雜。甚至有時候設計模式還會把實作邏輯變得更複雜。
但這些東西的目的本來就不在實作邏輯上,而是在抽象概念,意圖表達上。
設計是抽象的….
Comments