vlambda博客
学习文章列表

C语言设计模式--建设者模式

前言

这里先插一点题外话,在C语言中,实现封装、继承、隐藏、多态等等特性,是完全没有问题的。但是在使用过程中,必定是不如自带这些特性的语言方便好用的,比如C++\java等。

一旦要通过C语言来实现各种设计模式,必定会在严谨地维护类层次上造成非常繁琐和臃肿的代码。这是因为C++/java等自带一套面向对象的工具,而C语言要在代码设计中,不断地根据实际情况来创造面向对象的工具。

更多深层次的原因欢迎阅读我的文章:

因此对于文章的内容,请大家主要关注、体会设计模式本身,更多地去考虑程序的可扩展性和可维护性。

文章本身是希望向C语言爱好者普及设计模式的知识,在所有的程序设计中,本意都是尽可能地简单易懂。因此抛开设计模式的本意去讨论程序的代码实现优劣,是没有任何意义的。之前发表的c语言面向对象系列文章确实参考了网上很多资料和书籍,也借鉴了其中一些案例。但是代码确实是自己完整实现的,并复制粘贴在菜鸟 C 在线工具进行验证了(https://c.runoob.com/compile/11)。今后类似的借鉴、抄袭等等话题不想再次和大家争论,容易伤和气。毕竟只是希望大家能从我的文章有所收获而已,不喜欢直接右上角关闭就好。

鉴于之前有部分读者过于关注程序本身的功能性,更有甚者仅因为某一两方面见解不同就口出恶言。唯恐这样下去只会把技术的圈子搞臭。因此以后的c语言设计模式将不再给出具体实现,只讲解模式动机和设计思路。

希望大家可以针对设计模式方面给文章提出一些改进的建议,本人可以保证,对于无恶意、有针对性的建议,一定会吸收接纳、及时改正。

接下来是文章的正文部分。

模式动机

无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

场景:使用程序画一个小人,要求有头、身体、两手、两脚。

传统代码实现:

void draw_head()
{
...
}
void draw_body()
{
...
}
void draw_hand()
{
...
}
void draw_foot()
{
...
}
//主程序
void main()
{
draw_head();
draw_body();
draw_hand();
draw_foot();
}

主程序在画小人的时候,需要知道构造小人的所有具体函数,一旦小人的身体特征发生变化(例如一个瘦子变成了胖子),就必须更改主程序的函数调用。但是主程序关心的是小人这一个完整的对象,而不是关心小人的具体构建过程。

解决方案

使用建设者模式,封装复杂对象的创建过程,用户无须关心该对象所包含的属性以及它们的组装方式。

#include <stdio.h>
//定义小人构建的抽象接口
typedef struct Persion
{
void (*build_head)();
void (*build_body)();
void (*build_hand)();
void (*build_foot)();
void (*build_persion)(struct Persion* p);
}s_persion;

//定义瘦小人构建的接口
void draw_thin_head()
{
...
}
void draw_thin_body()
{
...
}
void draw_thin_hand()
{
...
}
void draw_thin_foot()
{
...
}

//定义胖小人构建的接口
void draw_fat_head()
{
...
}
void draw_fat_body()
{
...
}
void draw_fat_hand()
{
...
}
void draw_fat_foot()
{
...
}


//主程序
void main()
{
//定义构建小人抽象接口
s_persion* people;
//利用胖小人或者是瘦小人的具体接口来初始化抽象接口
...
//调用抽象接口中的构建小人方法
people->build_persion(s_persion* people);
}

修改后的主程序,不需要关心小人的具体构建过程,直接通过抽象接口中的构建方法即可获取相应的对象。当小人的特征发生变化,或者构建步骤发生变化,均无需改动主程序。提高了程序的可维护性和可拓展性。