一种典型C语言开发思维及其可能的问题

来源:互联网 发布:舞蹈女生 知乎 编辑:程序博客网 时间:2024/05/30 23:03
先来看一个开发实例:
静态内存模块主要功能是根据指定的(模块号,所需内存大小)从某个固定的区域划分内存,在运行过程中不会有申请释放操作。
如有一块5M的内存空间addr,分别有5个模块,每个模块所需的内存为(1MB,2MB,1MB,0.5MB,0.5MB),则各模块分到的地址如下
(addr,addr+1MB, addr+3MB, addr+4MB,addr+4.5MB)。当然还有其他的一些扩展功能如对齐,模块间隔离等等。


开始的实现时,先定义一个配置表结构体,结构体有如下成员:模块号,模块要求内存大小,分配到的地址,其他如对齐要求等。

然后定义一个配置表的全局变量gastrCfgTbl,初始化函数里根据这些配置信息进行内存划分,初始化函数如下:
int static_mem_init(void *pulStaticMem, unsigned long ulSize)
{
static_mem_cfg_tbl *pstrCfgTbl = gastrCfgTbl;
...
//按照配置表信息进行内存划分。
}
void *static_mem_alloc(int ulModule)
{
static_mem_cfg_tbl *pstrCfgTbl = gastrCfgTbl;
...
//返回已经划分好的配置表内存地址。
return pstrCfgTbl->addr;
}


这个设计初看并没有什么问题,功能也完成的很好。
但过了不久,新的需求来了(可恶,为什么需求老变),希望增加一个新的配置表,在另外一块静态内存空间里进行划分(为什么这个配置表与
老的配置表不能合并,不在本话题讨论之内)。
由于已有的函数与全局配置表信息是耦合在一起的,代码没有变法重用。怎么办呢?很简单,把原来代码里写死的全局变量抽取出来,作为一
个函数的参数传递进去,如下:


int static_mem_init(void *pulStaticMem, unsigned long ulSize, static_mem_cfg_tbl *pstrCfgTbl)
{
//按照配置表信息进行内存划分。
}
void *static_mem_alloc(static_mem_cfg_tbl *pstrCfgTbl, int ulModule)
{
//通过配置表获取模块对应的地址并返回。
}


配置表1和2分别调用static_mem_init并把当前配置表信息传入,代码就从数据耦合变成了接口耦合(接口使用了配置表结构)了,耦合程度降
低了。如下:
int config_table_xxx_init(void)
{
return static_mem_init( static_mem_xxx, xxx_size, pstrCfgTblxxx);
}
int config_table_yyy_init(void)
{
return static_mem_init( static_mem_yyy, yyy_size, pstrCfgTblyyy);
}


修改后的静态内存模块是一个抽象的实现,可以实现重用,增加新的需求也非常简单。比如要求静态内存不是在代码的配置表写死的,而是通

过某个配置文件读取,改动非常简单,增加一个读取配置文件的函数,把配置信息读取出来后传给static_mem_init。
int config_static_mem_by_xml(const char *xml)
{
get_cfg_from_file(xml, cfg_tbl);
return static_mem_init( static_mem_addr, size, cfg_tbl);
}




从这个例子可以看到,一种很典型的C语言开发思维:先设计这个模块的结构体,定义这个结构体的全局变量,然后的功能代码就围绕着这个全
局变量开展。这是很过程式的一种思维,为了完成目标,按照步骤1,2,3开展,最后目标达到了,但带来的问题就是代码不够内聚,存在数据
耦合,不能重用。


而修改后的代码,熟悉面向对象的可能感觉非常眼熟,比如:

class MyClass
{
...
}
int init_x(void)
{
object = new MyClass(x)
object->do_something();
}


int init_y(void)
{
object = new MyClass(y)
object->do_something();
}



从这个实例,给我们带来的启示就是:

1,需求一定要做抽象,需求是一个具体的应用,是易变的,僵化的,不能一拿到需求就冲着目标完成了事。

2,逻辑功能实现与应用分开。记得学习数据结构时,有一个说法是代码=数据结构+算法,全局变量是数据结构的一种实体,不是一个抽象的概
念。因此,在开发时应该抑制全局变量的使用冲动。代码的实现(算法)围绕着抽象的数据结构展开,而不是围绕一个具体的全局变量展开。
先把逻辑功能实现了,再在上面添加具体的应用。这样在实现逻辑功能时,只需要考虑实现上的正确性,而不需要考虑具体应用上的细枝末节
,代码的正确性更容易保证;到了具体应用时,只需要考虑应用场景,不需要考虑逻辑功能实现,也更能把握应用场景的准确性。


3,抑制全局变量的使用冲动。这里感觉有必要再重复一次,如果在开发逻辑功能代码时,发现需要添加全局变量才能解决问题,则需要考虑下

代码的设计是否足够抽象,是否非加不可。