## 数据再抽象
上一节的代码有些冗长,我们可以尝试对其进行精简。首先看下面这三个结构体及其 `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;
}
~~~