## 山重水復疑無路
經過再次重構后的?`create_chain_node`?看上去要好了一些,但是依然有兩段代碼存在高度重復:
~~~
struct pair *left_center = pair_for_double_type(1.0, 1.0);
double *left_radius = malloc(sizeof(double));
*left_radius = 0.5;
struct pair *left_hole = malloc(sizeof(struct pair));
left_hole->first = left_center;
left_hole->second = left_radius;
struct pair *right_center = pair_for_double_type(9.0, 1.0);
double *right_radius = malloc(sizeof(double));
*right_radius = 0.5;
struct pair *right_hole = malloc(sizeof(struct pair));
right_hole->first = right_center;
right_hole->second = right_radius;
~~~
但是僅從?`pair`?結果體層面已經無法對這兩段代碼進行簡化了,而且我又非常不想寫一個像下面這樣的輔助函數:
~~~
struct pair *
create_hole(struct pair *center, double radius)
{
struct pair *ret = malloc(sizeof(struct pair));
double *r = malloc(sizeof(double));
*r = radius;
ret->first = center;
ret->second = r;
return ret;
}
~~~
雖然?`create_hole`?能夠將上述兩段重復的代碼簡化為:
~~~
struct pair *left_center = pair_for_double_type(1.0, 1.0);
struct pair *left_hole = create_hole(left_center, 0.5);
struct pair *right_center = pair_for_double_type(9.0, 1.0);
struct pair *right_hole = create_hole(right_center, 0.5);
~~~
但是與?`pair_for_double_type`?函數相比,`create_hole`?這個函數的應用范圍非常狹小。由于?`pair_for_double_type`?函數可以將兩個?`double`?類型的數據存儲到?`pair`?結構體中,在我們的例子中創建二維點與矩形可以用到它,在科學計算中創建極坐標、復數以及所有的二次曲線方程式也都都能用到它,但是?`create_hole`?卻只能在創建車鏈這件事上有點用處。也就是說,正是因為?`pair_for_double_type`?函數所取得的成功,導致我們認為?`create_hole`?的品味太低。我們應該想一想還有沒有其他途徑可以消除上述代碼的重復。
仔細分析?`left_hole`?與?`right_hole`?的構造過程,不難發現?`hole`?的?`center`?與?`radius`?這兩種數據的類型不一致是造成我們難以對上述重復的代碼進行有效簡化的主要原因,`create_hole`?之所以能夠對上述重復的代碼進行大幅簡化,是因為它根據我們的問題構造了一個特殊的?`pair`?結構體——姑且稱之為 X。X 結構體的特殊指出在于其?`first`?指針存儲的是一個面向?`double *`?的同構類型的?`pair`?結構體,其?`second`?指針則存儲了一個?`double`?類型數據的基地址。正是因為 X 的結構太特殊了,所以導致?`create_hole`?這種抽象的應用范圍過于狹隘,以至于現實中只有圓形比較符合這種結構體。
既然是異構的?`pair`,而我們已經實現了一個可以創建存儲?`double`?類型數據的?`pair`?的函數?`pair_for_double_type`,這個函數的結果是可以直接存入異構?`pair`?中的。現在我們缺少只是一個可以將?`double`?值轉化為可直接存入異構?`pair`?的函數,即:
~~~
double *
malloc_double(double x)
{
double *ret = malloc(sizeof(double));
*ret = x;
return ret;
}
~~~
有了這個函數,就可以對?`create_chain_node`?繼續進行簡化了:
~~~
struct chain_node *
create_chain_node(void)
{
struct pair *left_hole = malloc(sizeof(struct pair));
left_hole->first = pair_for_double_type(1.0, 1.0);;
left_hole->second = malloc_double(0.5);
struct pair *right_hole = malloc(sizeof(struct pair));
right_hole->first = pair_for_double_type(9.0, 1.0);;
right_hole->second = malloc_double(0.5);
struct pair *holes = malloc(sizeof(struct pair));
holes->first = left_hole;
holes->second = right_hole;
struct pair *body = pair_for_double_type(10.0, 1.0);
struct pair *shape = malloc(sizeof(struct pair));
shape->first = body;
shape->second = holes;
struct chain_node *ret = malloc(sizeof(struct chain_node));
ret->prev = NULL;
ret->next = NULL;
ret->shape = shape;
return ret;
}
~~~
而且,基于?`malloc_double`?函數,還能對?`pair_for_double_type`?函數進行簡化:
~~~
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;
}
~~~
事實上,如果我們再有一個這樣的函數:
~~~
struct pair *
pair(void *x, void *y)
{
struct pair *ret = malloc(sizeof(struct pair));
ret->first = x;
ret->second = y;
return ret;
}
~~~
還能對?`reate_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;
}
~~~
看到了吧,只要略微換個角度,很多看似難以簡化的代碼都能得以簡化。這個簡化的過程一直是在指針的幫助下進行的,但事實上,當你的注意力一直集中在怎么對代碼進行簡化時,指針的使用簡直就是本能一樣的存在,以至于你覺得你并沒有借助指針的任何力量,完全是你自己的邏輯在指導著你的行為。在這個過程中,無論是面向對象還是面向模板,都很難將你從冗長的代碼中拯救出來……