less than 1 minute read

是,也不是。

先從個例子說起。

前陣子在重構羅賓的 Side Project – 一個遊戲繪圖引擎 – 發現了一個應該要調整的問題。

攝影機 Frustum 的視野角度 (Field of View),羅賓是用原始的浮點數型態去做設定,像這樣:

class Frustum
{
public:
  void fov(float value);
  .....
}; 

當下的直覺,這個 value 的單位應該是弳度(Radian),而不是角度(Degree)。但是,也不是很肯定,所以就深入進實作程式碼去看看。

果然是弳度。

發現這個函式有更好的表達意圖的方式之後,羅賓便決定要來點小重構。

最簡單的方式是,把 value 改成 value_in_radian。但這其實並沒有改善多少,使用端還是會用浮點數型態來呼叫,”必須使用弳度”的知識與責任,仍然在呼叫端身上。

另一種方式是,建立兩個小小的值物件(Value Object),RadianDegree

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