-
什么是依赖倒置
依赖倒置原则,英文缩写DIP,全称Dependence Inversion Principle。原始定义:High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。
官方翻译:上层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
也就是要依赖确定的东西,对于软件,确定的东西就是抽象就是接口。代码开发时,从现实逻辑出发:上层依赖底层各个模块,代码自然就会写成上层依赖底层的样子。这样虽然很直白,但是底层代码变化很可能会引起上层代码的被动变化,而修改的越多,bug的可能性就越大。
如果我们将上层依赖底层的关系倒置:底层依赖上层,接口的概念就出现了。下面通过一个模拟题目来实践一下依赖倒置原则
业务有个固定的初始化流程顺序:sys->driver->memory 请模拟实现一下
以下代码仅做示意 -
C语言依赖倒置的应用–V1.0
直白的功能实现// sys.c drive.c memory.c u32 sys_init(void) {} u32 drive_init(void) {} u32 memory_init(void) {} // init.c #include "sys.c" #include "drive.c" #include "memory.c" void init(void) { sys_init(); drive_init(); memory_init(); }
代码很清晰,但是如果需要新增其他流程或者需要将某一现有流程删除,
需要做的就是新增或删除1个.c并且对应修改init函数。当项目变得复杂,模块众多的时候,修改init函数甚至修改init.c文件的动作都充满了风险。 -
C语言依赖倒置的应用–V1.1 降低风险
通过新增init_modify.c暴露部分原有init.c的功能,来实现对上层尽可能小的影响:仅需修改init_modify.c// sys.c drive.c memory.c u32 sys_init(void) {} u32 drive_init(void) {} u32 memory_init(void) {} // init.c #include "init_modify.h" void init(void) { for (s32 i = 0; i < sizeof(fun_list)/sizeof(fun_list[0]; i++) { fun_list[i](); } } // init_modify.c #include "sys.c" #include "drive.c" #include "memory.c" void (*fun_list[])() = { sys_init, drive_init, memory_init, }
接口抽象仍然不足,仍有底层修改影响上层的风险
-
C语言依赖倒置的应用–V2.0
如下实现可以保证在新增或删除模块时,仅通过新增删除对应.c即可,完全不用修改上层文件init.c
原理就是利用链接器可以将数据固定在指定范围内。
缺点也很明显:不易理解,代码量增加。// *.ld SECTIONS { ... .module_init: { ___module_init_start = .; KEEP(*(.module_init)) __module_init_end = ,; . = ALIGN(4); } > DATA ... } // module_init.h typedef unsigned int u32 typedef signed int s32 typedef u32 (*initFunc)(void); struct module_init_para { u32 module_level; initFunc func; } enum module_init_level { MODULE_SYS_LEVEL = 0, MODULE_DRIVE_LEVEL = 1 MODULE_MEMORY_LEVEL = 2, }; // sys.c u32 sys_init(void) {} static struct module_init_para m_sys = {MODULE_SYS_LEVEL, sys_init}; static void *p_m_sys __used __attribute__(section(".module_init")) = &m_sys; // init.c extern u32 __module_init_start; extern u32 __module_init_end; static void _swap(struct module_init_para **a, struct module_init_para **b) { struct module_init_para *tmp; tmp = *a; *a = *b; *b = tmp; } void sort_modules(void) { struct module_init_para **p = (struct module_init_para **)&__module_init_start; s32 cnt = &__module_init_start - &__module_init_end; s32 i ,j; for (i = 0; i < cnt - 1; i++) { for (j = i + 1; j < cnt; j++) { if (p[i]->level > p[j]->level) _swap(&p[i], &p[j]); } } } void start_module_init(void) { struct module_init_para **p; sort_modules(); for (p = (struct module_init_para **)&__module_init_start; p != (struct module_init_para **)&__module_init_end; p++) { (*p)->func(); } } void init(void) { start_module_init(); }
-
C语言依赖倒置的应用–V2.1
进一步优化// module_init.h #define MODULE_INIT(mLevel, function) \ static struct module_init_para m_##function = { \ .module_level = mLevel, \ .func = function, \ }; \ static void *p_##function __used __attribute__(section(".module_init")) = {&m_##function} // sys.c u32 sys_init(void) {} module_INIT(MODULE_SYS_LEVEL, sys_init);
设计模式之依赖倒置 C语言版本
于 2023-08-26 12:07:53 首次发布