OOP封装-隔离变化

封装 —— 保护程序的隐私, 不该让调用者知道的事,坚决不能暴露出来。

为什么要封装

  • 隔离变化

    程序的隐私通常是程序最容易变化的部分,比如内部数据结构、内部使用的函数和全局变量等,需要把这些代码封装起来,从而让它们变化不会影响系统其他部分

  • 降低复杂度

    接口最小化是软件设计基本原则之一,最小化接口最容易被理解和使用,所以封装内部细节,只保留用户需要的最小接口。

如何封装

  • 隐藏数据结构

    图方便直接访问数据结构的成员,容易造成模块之间紧密耦合,从而给以后的修改带来困难。

    • 内部使用的数据结构,外部完全不会应用,定义在C文件中即可,不要放在头文件中
    • 如果数据结构内外都要使用,则在头文件暴露数据结构

    具体实现细节做法:

    • 在头文件中声明该数据结构
    • 在C文件中定义该数结构
    • 提供操作该数据结构的函数。哪怕只是存取数据结构的成员,也要包装成相应的函数
  • 隐藏内部函数

    • 内部函数通常实现一些特定算法(如果通用,应该放到一个公共函数库里),而这些是接口使用者不需要知道的内部细节。
    • 不隐藏内部函数函数名也会污染全局名字空间,可能造成重名
    • 不隐藏内部函数容易诱导调用者绕过正规接口走捷径,造成代码不必要的耦合。

    隐藏内部函数的做法

    • 在头文件中,只放最少的接口函数的声明
    • 在C文件中,所有内部函数都加上static关键字限定在本文件使用
  • 禁用全局变量

    当把程序从单线程该为多线程,对并发的程序产生不利影响。更重的是直接使用全局变量,会造成调用者和实现者之间的耦合。

——— 学习摘录自《系统程序员成长计划》


隔离变化

像C++具有OOP语法糖的编程语言中可以直接使用虚函数/纯虚函数来隔离变化。
对于C语言没有这样的语法糖。
而C语言中也有隔离变化的法宝 ---- 函数指针。可以当作C语言的虚函数。

扫描二维码关注公众号,回复: 12911589 查看本文章

不止在OOP起大作用,在简单的程序中也可以很好的起到隔离变化的作用。

简单的隔离变化示例:检测风扇两种状态

刚开始的函数:

void Fand_Detect(void)
{
    
    
	static uint16_t count = 0;
	
	switch (fStatus)
	{
    
    
		case FAN_IS_RUN:
		{
    
    
			if (FAN_DETECT_INPUT() == FAN_STOP)
			{
    
    
				count++;
				if (count >= FAN_IS_STOP_COUNT)
				{
    
    
					count = 0;
					fStatus = FAN_IS_STOP;     
					SendErrCode(CCU_FAN_IS_STOP);                         
				}
			}
			else                                                           
			{
    
    
				count = 0;
			}
			break;
		}
		case FAN_IS_STOP:
		{
    
    
			if (FAN_DETECT_INPUT() == FAN_RUN)
			{
    
    
				count++;
				if (count >= FAN_IS_RUN_COUNT)
				{
    
    
					count = 0;
					fStatus = FAN_IS_RUN;     
					SendErrCode(CCU_FAN_IS_RUN);                         
				}
			}
			else                                                           
			{
    
    
				count = 0;
			}
			break;
		}
	}
}

可以看出上述程序两种状态下的检测代码是类似,变化的地方有if (FAN_DETECT_INPUT() == FAN_RUN)fStatus = FAN_IS_STOP; SendErrCode(CCU_FAN_IS_STOP)

使用函数指针隔离变化,修改程序如下:

static void _FanIsStopCallback(void)
{
    
    
	fStatus = FAN_STOP_STATE;
	printf("Fan Is Stop\r\n");
	SendErrCode(CCU_FAN_IS_STOP);                            //send error code that fan is stop
}

static void _FanIsRunCallback(void)
{
    
    
	fStatus = FAN_RUN_STATE;
	printf("Fan Is Run\r\n");
	SendRestoreCode(CCU_FAN_IS_RUN);      //send error code that fan is run
}

static void _Fan_Detect(enum FanLevel level, uint16_t maxCount, void (*callback)(void))
{
    
    
	static uint16_t count = 0;
	
	if (FAN_DETECT_INPUT() == level)
	{
    
    
		count++;
		if (count >= maxCount)
		{
    
    
			count = 0;
			if (callback)
				callback();
		}
	}
	else                                                 
	{
    
    
		count = 0;
	}
}

void Fand_Detect(void)
{
    
    
	static uint16_t count = 0;
	
	switch (fStatus)
	{
    
    
		case FAN_RUN_STATE:
		{
    
    
			_Fan_Detect(FAN_STOP, FAN_IS_STOP_COUNT, _FanIsStopCallback);
			break;
		}
		case FAN_STOP_STATE:
		{
    
    
			_Fan_Detect(FAN_RUN, FAN_IS_RUN_COUNT, _FanIsRunCallback);
			break;
		}
	}
}

避免了写两段类似的重复的程序。


OOPC方式重写fan detect

抽象fan:

struct AbstractFan {
    
    
	uint16_t count;
	uint8_t fStatus;
	int (*detect)(struct AbstractFan *thiz, uint8_t *status);
	void (*stop)(struct AbstractFan *thiz);
	void (*run)(struct AbstractFan *thiz);
};

static inline void _AbFan_Detect(struct AbstractFan *thiz, uint8_t level, uint16_t maxCount)
{
    
    
	uint8_t status;
	 
	if (thiz->detect)
    	thiz->detect(thiz, &status);
    
    if (status == level)
    {
    
    
		if (thiz->count++ >= maxCount)
		{
    
    
			thiz->count = 0;
			if (level== FAN_RUN)
			{
    
    
				if (thiz->run)
					thiz->run(thiz);
				thiz->fStatus = FAN_RUN_STATE;
			}
			else
			{
    
    
				if (thiz->stop)
					thiz->stop(thiz);
				thiz->fStatus = FAN_STOP_STATE;
			}
		}
	}
	else 
	{
    
    
		thiz->count = 0;
	}
}

static inline void AbFan_Detect(struct AbstractFan *thiz)
{
    
    
	if (thiz == NULL)
		return;
    
	switch(thiz->fStatus)
	{
    
    
		case FAN_IS_RUN:
		{
    
    
			_AbFan_Detect(thiz, FAN_STOP, 40);
			break;
		}
		case FAN_IS_STOP:
		{
    
    
			_AbFan_Detect(thiz, FAN_RUN, 10);
			break;
		}
	}
}

static inline void AbFan_SetStatus(struct AbstractFan *thiz, uint8_t status)
{
    
    
	if (thiz == NULL)
		return;
		
	thiz->fStatus = status;
}

fan类:

struct fan {
    
    
	struct AbstractFan *fan;
}

//构建注入依赖的抽象接口
static inline void Fan_Init(struct fan *thiz, struct AbstractFan *f)
{
    
    
	if (thiz == NULL || f == NULL)
		return;
		
	thiz->fan = f;
	f->count = 0;
	AbFan_SetStatus(f, FAN_IS_RUN);
}

static inline void Fan_Dectect(struct fan *thiz)
{
    
    
	uint8_t status;
	
	if (thiz == NULL)
		return;
			
	AbFan_Detect(thiz->fan);
}

实例化伪代码:

static void _detect(struct AbstractFan *thiz, uint8_t *status)
{
    
    
	*status = (uint8_t )FAN_DETECT_INPUT();
}

static void _run(struct AbstractFan *thiz)
{
    
    
	printf("Fan Is Run\r\n");
	SendRestoreCode(CCU_FAN_IS_RUN); 
}

static void _stop(struct AbstractFan *thiz)
{
    
    
	printf("Fan Is Stop\r\n");
	SendErrCode(CCU_FAN_IS_STOP); 
}

static struct AbstractFan abFan = {
    
    
	.detect = _detect,
	.run = _run,
	.stop = _stop,
};

static struct fan fan;

void main(void)
{
    
    
    Hardward_Init();
	Fan_Init(&fan, &abFan);
	
	while (1)
	{
    
    
		Fan_Detect(&fan);
	}
}

…emm…有点复杂…单片机上还是用函数指针当函数参数的方法隔离好

猜你喜欢

转载自blog.csdn.net/qq_36413982/article/details/112548972
OOP