## 面向什么,可能就會失去未面向的那些
在上文中模擬車鏈的程序中,我一開始是用面向對象的方式來寫的,所以我造出了 5 個結構體,分別描述了二維點、矩形、圓形、鏈節形狀以及鏈節等對象,結果卻出現了一大堆繁瑣的代碼。雖然面向對象編程,在思維上是非常簡單的,那就是現實中有什么,我們就模擬什么。但是你認真思考一下,現實中其實很多東西都有共性,如果你傻乎乎的去逐個模擬,而忽略它們的共性,那么你的代碼絕對會非常臃腫。
當然,面向對象編程也提倡從所模擬的事物中提取共性,然后借助繼承的方式來簡化代碼。但是一旦信仰了類與繼承,你能做的最好的抽象就是對某一類事物進行抽象,比如你能夠對『車』類的事物進行抽象,但是你卻無法將對『飛機』和『車』這兩類中的事物進行抽象。顯然,飛機與車是有共性的,例如它們都能載客,都有儀表盤,都有窗戶,都有座位,都有服務員……
當我發現基于面向對象創造的那些結構體存在著一個共性——它們都包含著兩個成員,很自然的就會想到我應該制造一個包含著兩個任意類型的結構體?`pair`,然后用?`pair`?來容納我需要的數據。當面向對象編程范式在你的思想中根深蒂固,這種簡單的現象往往會被忽略的,特別是你已經滿足于你寫的程序已經能夠成功的運行之時。
接下來,當我試圖用?`pair`?結構體取代二維點、矩形、圓形、鏈節形狀等結構體的時候,我就開始走上了『泛型』的道路。C 語言里沒有 C++ 模板這種工具可以用,所以我只能依賴?`void *`,而且為了簡化?`double`?類型的數據向?`void *`?的轉化,所以定義了:
~~~
double *
malloc_double(double x)
{
double *ret = malloc(sizeof(double));
*ret = x;
return ret;
}
struct pair *
pair_for_double_type(double x, double y)
{
struct pair *ret = malloc(sizeof(struct pair));
ret->first = malloc_double(x);
ret->second = malloc_double(y);
return ret;
}
~~~
如果你對 C++ 的泛型編程有所了解,一定會覺得?`pair_for_double_type`?函數其實就是對?`pair`?進行特化。因為本來我是希望?`pair`?能存儲任意類型的數據的,但是現在我需要頻繁的用它來存儲一對?`double`?類型的數據,那么我就應該去制造一個專用的?`pair`?結構。
當我發現我需要頻繁的產生?`pair`?實例,并向它的?`first`?與?`second`?指針中存儲某些類型的數據存儲空間的基地址,所以我就將這種共性抽象為:
~~~
struct pair *
pair(void *x, void *y)
{
struct pair *ret = malloc(sizeof(struct pair));
ret->first = x;
ret->second = y;
return ret;
}
~~~
最終使得?`create_chain_node`?函數的定義即簡潔又清晰:
~~~
struct chain_node *
create_chain_node(void)
{
struct pair *left_hole = pair(pair_for_double_type(1.0, 1.0), malloc_double(0.5));
struct pair *right_hole = pair(pair_for_double_type(9.0, 1.0), malloc_double(0.5));
struct pair *holes = pair(left_hole, right_hole);
struct pair *body = pair_for_double_type(10.0, 1.0);
struct pair *shape = pair(body, holes);
struct chain_node *ret = malloc(sizeof(struct chain_node));
ret->prev = NULL;
ret->next = NULL;
ret->shape = shape;
return ret;
}
~~~
原來我用面向對象編程范式所寫的代碼是 104 行,換成泛型編程范式所寫的代碼是 75 行。那么我可以斷定,是泛型編程拯救了面向對象嗎?當然不能!因為我們的程序還沒有寫完,我們還需要面向對象。