本帖最后由 qiao--- 于 2023-12-23 16:20 编辑
前言
在我的上期测评中,在搭建工程时候遇到了点小问题,后面经过对比官方的工程发现没有勾选上下面这个选项
因为我用的是AC6,如果不选这个,编译器就会不包容一些AC5的一些机制,进而就会报出警告,所以勾选上这个现在的工程就没有问题啦。
我们在开发一些比较大的工程时,降低程序的耦合度非常重要,因为它关系到程序后期的维护,这一期就讲一下低耦合思想的运用。
1.认识低耦合编程思想
低耦合编程思想是一种设计和编写代码的理念,旨在降低不同模块或组件之间的依赖程度。具体来说,低耦合编程思想通过尽量减少模块之间的相互依赖,使得每个模块都能独立地进行开发、测试和维护。这种设计方式可以提高代码的灵活性、可维护性和可重用性,降低系统的复杂度和风险。
低耦合编程思想的作用包括:
- 提高代码的模块化和可重用性:模块之间的低耦合性可以使得它们更容易被重用于不同的上下文中。
- 降低系统的复杂度:减少模块之间的依赖可以降低系统的复杂度,使得系统更容易理解和维护。
- 提高代码的灵活性:低耦合的代码更容易进行修改和扩展,因为修改一个模块不会对其他模块造成影响。
- 提高代码的可测试性:低耦合的代码更容易进行单元测试,因为每个模块都可以独立地进行测试。
2.运用低耦合思想
我们先把代码添加上,再讲原理
(1)首先在main.c中添加以下代码
LoopFunction loopFunction[MAX_INIT_FUNCTIONS]; //函数地址的数组
int loopFunctionCount = 0; //相当于指针
//以此执行函数数组中的函数
void callInitFunctions(void){
uint8_t i ;
for(i = 0 ; i<loopFunctionCount;i++){
loopFunction[i]();
}
}
在main.h中添加以下代码
typedef void (*LoopFunction)(); //定义函数类型
#define MAX_INIT_FUNCTIONS 10
extern LoopFunction loopFunction[];
extern int loopFunctionCount;
#define INIT_FUNCTION(name) \
void name(); \
void __attribute__((constructor)) init_##name(){ \
loopFunction[loopFunctionCount++] = &name; \
} \
void name()
原理:我们在main.c中添加了一个装函数地址的数组,数组的最大容量为10。而loopFunctionCount相当于一个指针,它永远指向数组中最后一个函数的下标,这样方便我们添加新的函数和统计函数个数。 在callInitFunctions函数中我们利用循环依次调用每个函数就可以了。 这就相当我们现在有一个容器,我们只需要向这个容器中丢函数就行了,而容器自己会进行调用容器中的函数。
既然容器有了,他也会自己调用了,我们怎么来“丢函数”呢? 下面这个宏定义做了这件事情
#define INIT_FUNCTION(name) \
void name(); \ //1
void __attribute__((constructor)) init_##name(){ \ //2
loopFunction[loopFunctionCount++] = &name; \ //3
} \ //4
void name() //5
在这个宏定义中 ,使用了GCC的构造函数特性__attribute__((constructor)),
这个特性可以将函数标记为在程序启动时自动执行的构造函数。这意味着,当程序启动时,这些构造函数会在main函数之前自动执行,而不需要显式调用。这个宏定义在第一行先声明了一下传进来的name函数,然后在第二行使用void __attribute__((constructor)) init_##name()初始化了一个init_name()函数,后面2到3行的{}之间就是init_name()的具体实现(‘##’用于连接init_和name),这个init_name()将会在调用name函数之前隐式的调用一次。最后在第5行再次声明一下name函数。
我们看到在init_name()中添加了我们指定函数到容器里,至此“丢函数”的功能也已经实现。
下面我们来测试一下。在main函数中,我们调用callInitFunctions函数。
我们在新建3个文件,分别为task_1,task_2,task_3,在3个文件中分别写上下面的代码。
现在我们将程序烧录到板子里观看效果。
可以看到依次执行了我们的三个函数。 那现在问题来了,降低耦合体现在哪呢?当我们用不到某个文件的程序时候,我们可以直接将那个文件删掉,而不需要改变其中的代码,进而可以更好地维护程序。那我们试一试直接删除掉task_3.c文件,看看程序能不能正常运行。
在我的代码中已经直接把task_3.c文件删除了,而并没有改变我的代码。我们运行试一试。
现在只有两个任务在运行了。
可能有些对rtt_thread实时系统属性的朋友会觉得似曾相识,没错,是不是有点像RT_thread里面的APP_INIT_EXPORT()等宏定义呢
总结:通过引用低耦合的编程思想在一些没有实时操作系统的工程里可以更好地维护代码,同时也方便代码的移植。