## 數據再抽象
上一節的代碼有些冗長,我們可以嘗試對其進行精簡。首先看下面這三個結構體及其?`create`?函數:
~~~
struct point {
double x;
double y;
};
struct rectangle {
double width;
double height;
};
struct circle {
struct point *center;
double radius;
};
struct chain_node_shape {
struct rectangle *body;
struct circle *holes[2] ;
};
struct point *
create_point(double x, double y)
{
struct point *ret = malloc(sizeof(struct point));
ret->x = x;
ret->y = y;
return ret;
}
struct circle *
create_circle(struct point *center, double radius)
{
struct circle *ret = malloc(sizeof(struct circle));
ret->center = center;
ret->radius = radius;
return ret;
}
struct rectangle *
create_rectangle(double w, double h)
{
struct rectangle *ret = malloc(sizeof(struct rectangle));
ret->width = w;
ret->height = h;
return ret;
}
struct chain_node_shape *
create_chain_node_shape(struct circle *c1,
struct circle *c2,
struct rectangle *rect)
{
struct chain_node_shape *ret = malloc(sizeof(struct chain_node_shape));
ret->body = rect;
ret->holes[0] = c1;
ret->holes[1] = c2;
return ret;
}
~~~
顯然,這些代碼長的太像了!那四個結構體都是存儲兩個成員的結構體,而相應的?`create`?函數也無非是將函數所接受的參數保存到結構體成員中。有沒有辦法用很少的代碼來表示它們?有!
既然每個結構體都保存 2 個成員,那么我們就先將上述代碼刪掉,然后定義一個?`pair`?類型的結構體:
~~~
struct pair {
void *first;
void *second;
};
~~~
在?`pair`?結構體中,我們用了兩個?`void *`?指針,只有如此我們方能很自信的說?`pair`?可以存儲任意類型的兩個數據。接下來,只需修改?`create_chain_node`?函數的定義:
~~~
struct chain_node *
create_chain_node(void)
{
double *left_x = malloc(sizeof(double));
double *left_y = malloc(sizeof(double));
*left_x = 1.0;
*left_y = 1.0;
struct pair *left_center = malloc(sizeof(struct pair));
left_center->first = left_x;
left_center->second = left_y;
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;
double *right_x = malloc(sizeof(double));
double *right_y = malloc(sizeof(double));
*right_x = 9.0;
*right_y = 1.0;
struct pair *right_center = malloc(sizeof(struct pair));
right_center->first = right_x;
right_center->second = right_y;
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;
struct pair *holes = malloc(sizeof(struct pair));
holes->first = left_hole;
holes->second = right_hole;
struct pair *body = malloc(sizeof(struct pair));
double *width = malloc(sizeof(double));
*width = 10.0;
double *height = malloc(sizeof(double));
*height = 2.0;
body->first = width;
body->second = height;
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;
}
~~~
我勇敢的承認這個基于?`struct pair`?的?`create_chain_node`?函數太丑陋了,但是我們總算是消除了大量的結構體及其構造函數了,而且整體代碼量減少了大約 1/6。
仔細觀察上述代碼,顯然下面的三段代碼存在著高度的重復:
~~~
double *left_x = malloc(sizeof(double));
double *left_y = malloc(sizeof(double));
*left_x = 1.0;
*left_y = 1.0;
struct pair *left_center = malloc(sizeof(struct pair));
left_center->first = left_x;
left_center->second = left_y;
double *right_x = malloc(sizeof(double));
double *right_y = malloc(sizeof(double));
*right_x = 9.0;
*right_y = 1.0;
struct pair *right_center = malloc(sizeof(struct pair));
right_center->first = right_x;
right_center->second = right_y;
struct pair *body = malloc(sizeof(struct pair));
double *width = malloc(sizeof(double));
*width = 10.0;
double *height = malloc(sizeof(double));
*height = 2.0;
body->first = width;
body->second = height;
~~~
這三段代碼都在向?`pair`?結構體中存入兩個?`double *`?類型的數據。既然如此,我們可以專門寫一個函數,讓它生成面向`double *`?的?`pair`?結構體,即:
~~~
struct pair *
pair_for_double_type(double x, double y)
{
struct pair *ret = malloc(sizeof(struct pair));
double *first = malloc(sizeof(double));
double *second = malloc(sizeof(double));
*first = x;
*second = y;
ret->first = first;
ret->second = first;
return ret;
}
~~~
然后再次重構?`create_chain_node`?函數:
~~~
struct chain_node *
create_chain_node(void)
{
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;
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;
}
~~~