/ 用 C 语言编写建造者模式 /
/ 一、简介 /
建造者模式: 也称生成器模式,是 23 种设计模式中的一种,是一种创建型模式。适用情况:一个对象比较复杂,将一个对象的构建和对象的表示进行分离。
比较:与工厂模式进行对比,工厂模式不考虑对象部件组装过程,直接生成一个最终的对象,强调的是 结果 。而建造者模式先构建对象的每一个部件,然后再统一组建成一个对象,强调的是 过程。
目的:实现复杂对象的生产流程与部件进行解耦。
/ 二、设计实现 /
以建造房子为例,房子有公寓、别墅、楼房等类型,虽然是不同种类的房子,但其建造过程大体上都相同,例如都有这些流程:修建墙、窗户、门、地板、楼顶等。
我们实现 Builder(建造者)建房,因为都有修建(墙、窗户、门、楼等)这些部件,但是具体实现却不同,所以我们需要把实现这些组建的操作给抽象出来,把每个部件实现了,然后再组装起来,修建的房子就完成了。
设计建造者抽象接口如下通过指针函数与结构体进行抽象,如不了解,请参考上一篇介绍【用 C 语言实现简单工厂模式!】。
typedef struct IBuilder_t //建造者抽象接口
{
void (*make_floor)(void *obj); //修建地板
void (*make_door)(void *obj); //修建门
void (*make_wall)(void *obj); //修建墙
void (*make_window)(void *obj); //修建窗
void (*destory)(void *obj); //释放内存
House_t *house; //house对象
}IBuilder_t;
定义房子对象,然后房子里面的部件(墙、窗、门、地板等)交给建造者设置修建。
typedef struct House_t //定义房子要实现的接口
{
void (*setfloor)(struct House_t* obj, char *floor);
void (*setdoor)(struct House_t* obj, char *door);
void (*setwall)(struct House_t* obj, char *wall);
void (*setwindow)(struct House_t* obj, char *window);
char floor[32]; //地板名字
char door[32]; //门名字
char wall[32]; //墙名字
char window[32]; //窗名字
}House_t;
//修建的地板类型。
//static修饰,不让外部直接调用这个函数,
//一般这些函数的实现放到.c文件中,结构体定义在.h文件中,
//而是通过House_t结构体的setfloor()函数指针进行调用,起到封装效果(下同)。
static void house_setfloor(House_t* obj, char *floor)
{
if(obj) sprintf(obj->floor, "%s", floor);
}
//修建的门类型
static void house_setdoor(House_t* obj, char *door)
{
if(obj) sprintf(obj->door, "%s", door);
}
//修建的墙类型
static void house_setwall(House_t* obj, char *wall)
{
if(obj) sprintf(obj->wall, "%s", wall);
}
//修建的窗类型
static void house_setwindow(House_t* obj, char *window)
{
if(obj) sprintf(obj->window, "%s", window);
}
//构造函数 创建一个房子的对象
//此处这个函数不能适用static修饰
//该函数是要开放被外部调用的
House_t* constructor_house(void)
{
House_t* house = (House_t*)malloc(sizeof(House_t)); //申请对象
house->setdoor = house_setdoor; //函数指针赋值
house->setfloor = house_setfloor; //函数指针赋值
house->setwall = house_setwall; //函数指针赋值
house->setwindow = house_setwindow;//函数指针赋值
return house; //返回一个房子对象
}
接下来我们实现 builder,修建一个公寓。注:另外一个修建别墅实现,与修建公寓实现几乎完全一致,此处不做过多阐述。
//建造公寓的结构体,与IBuilder_t定义一致,即对IBuilder_t的实现
typedef struct FlatBuilder_t
{
void (*make_floor)(void *obj);
void (*make_door)(void *obj);
void (*make_wall)(void *obj);
void (*make_window)(void *obj);
void (*destory)(void *obj);
House_t *house;
}FlatBuilder_t;
//建造地板。static修饰作用同上
static void flat_make_floor(void *obj)
{
FlatBuilder_t* flat = (FlatBuilder_t*)obj;
flat->house->setfloor(flat->house, "修建:flat floor");
}
//建造门
static void flat_make_door(void *obj)
{
FlatBuilder_t* flat = (FlatBuilder_t*)obj;
flat->house->setdoor(flat->house, "修建:flat door");
}
//建造墙
static void flat_make_wall(void *obj)
{
FlatBuilder_t* flat = (FlatBuilder_t*)obj;
flat->house->setwall(flat->house, "修建:flat wall");
}
//建造窗
static void flat_make_window(void *obj)
{
FlatBuilder_t* flat = (FlatBuilder_t*)obj;
flat->house->setwindow(flat->house, "修建:flat window");
}
//释放内存的函数
static void flat_destory(void *obj)
{
FlatBuilder_t* flat = (FlatBuilder_t*)obj;
if(flat->house) free(flat->house); //首先先释放flat包含的house内存
flat->house=NULL;
free(flat); //其次再释放当前对象
flat=NULL;
}
//公寓建造者的构造函数,此处不能采用static修饰,因为其要被外部调用
FlatBuilder_t* constructor_flat_builder(void)
{
FlatBuilder_t* flat = (FlatBuilder_t*)malloc(sizeof(FlatBuilder_t));
flat->house = constructor_house();
flat->make_door = flat_make_door;
flat->make_floor = flat_make_floor;
flat->make_wall = flat_make_wall;
flat->make_window = flat_make_window;
flat->destory = flat_destory;
return flat;
}
/ 三、测试 /
main 函数里面的测试代码如下:定义一个 IBuilder_t *buidler=NULL,用它指向不同的建造者,实现修建不同的房子。此时修建公寓和别墅两种不同房子。
int main(void)
{
House_t *house=NULL;
IBuilder_t *buidler=NULL;
//指向公寓建造者,修建公寓
buidler=(IBuilder_t*)constructor_flat_builder();
buidler->make_door(buidler); //修建公寓的门
buidler->make_floor(buidler); //修建公寓的地板
buidler->make_wall(buidler); //修建公寓的墙
buidler->make_window(buidler);//修建公寓的窗
house = buidler->house; //拿到修建的好的房子
printf("%s\n", house->door); //显示公寓门
printf("%s\n", house->floor); //显示公寓地板
printf("%s\n", house->wall); //显示公寓墙
printf("%s\n", house->window);//显示公寓窗
buidler->destory(buidler); //释放内存
printf("\n");
//指向别墅建造者,修建别墅
buidler=(IBuilder_t*)constructor_villa_builder();
buidler->make_door(buidler); //修建别墅的门
buidler->make_floor(buidler); //修建别墅的地板
buidler->make_wall(buidler); //修建别墅的墙
buidler->make_window(buidler); //修建别墅的窗
house = buidler->house; //拿到修建好的房子
printf("%s\n", house->door); //显示别墅门
printf("%s\n", house->floor); //显示别墅地板
printf("%s\n", house->wall); //显示别墅墙
printf("%s\n", house->window); //显示别墅窗
buidler->destory(buidler); //释放内存
return 0;
}
执行的效果如下图:
/ 四、总结 /
通过上面的内容,我们了解到建造者模式主要是针对创建对象的过程的,而且还可以控制一个复杂类中部件创建的顺序以及部件创建的内容。提高程序的扩展性,践行“高内聚、低耦合”。
想要整个工程文件的小伙伴:在微信公众号【Linux编程用C】,在微信公众号回复designer即可获取。我是小C,欢迎大家关注、点赞支持,我们一起交流讨论学习!
PS:若大家想看C语言版本的其他设计模式,
请大家 点赞! 转发!关注!催更吧~~