【知识分享】C语言中的设计模式——解释器

背景

    解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。

名词释义

    解释器,其实就像正则表达式、SQL语言等一样,把一种特定的语言格式转换成另一个种语言格式。

C语言应用

    此模式更多应该吸收它的思维,个人最常用的就是把常用的逻辑语言进行变量提取,并转换成固定的逻辑格式。

例子

    现在来设计一个触摸显示屏的后台定时器功能,这个后台定时器需要实现一个功能,就是用户可以添加任意多个定时器,可以设定定时时间,定时触发条件,停止条件及执行动作。举个例子,用户可以设定当按下某个按键时,启动定时,定时每3s执行一次数据A加1的操作,当放开按键时,停止执行。
    先来看下普通的实现。

#include <stdint.h>

extern uint8_t KeySta;

uint8_t Cnt = 0;

/* 中断里调用,1s调用一次 */
void TmrTick(void)
{
    
    
	Cnt++;
	if  (Cnt > 200)
	{
    
    
		Cnt = 200;
	}
}

/* 用户操作 */
void UserFunc(void)
{
    
    
	A += 1;
}

int main(void)
{
    
    
	if (1 == KeySta)
	{
    
    
		if (Cnt >= 3)
		{
    
    
			UserFunc();
			Cnt = 0;
		}
	}
	else
	{
    
    
		Cnt = 0;
	}
}

    如果这时再加一个操作呢,比如用户要求按下按键2,定时每2s执行一次A-1的操作,放开按键2时停止执行。

#include <stdint.h>

extern uint8_t KeySta[2];

uint8_t Cnt[2] = 0;

/* 中断里调用,1s调用一次 */
void TmrTick(void)
{
    
    
	Cnt[0]++;
	if  (Cnt[0] > 200)
	{
    
    
		Cnt[0] = 200;
	}
	
	Cnt[1]++;
	if  (Cnt[1] > 200)
	{
    
    
		Cnt[1] = 200;
	}
}

/* 用户操作 */
void UserFunc1(void)
{
    
    
	A += 1;
}

void UserFunc2(void)
{
    
    
	A -= 1;
}

int main(void)
{
    
    
	/* 按键1的动作需求 */
	if (1 == KeySta[0])
	{
    
    
		if (Cnt[0] >= 3)
		{
    
    
			UserFunc1();
			Cnt[0] = 0;
		}
	}
	else
	{
    
    
		Cnt[0] = 0;
	}

	/* 按键2的动作需求 */
	if (1 == KeySta[1])
	{
    
    
		if (Cnt[1] >= 2)
		{
    
    
			UserFunc2();
			Cnt[1] = 0;
		}
	}
	else
	{
    
    
		Cnt[1] = 0;
	}
}

    如果用户再继续增加类似的按键功能,虽然每次复制代码就可以实现,但改动的地方还是很多,容易出现错误。这里我们来找找规律,上面增加的两个操作中,有哪些是不一样的?不难看出,这里触发条件、解除条件不同,定时的时间不同,执行动作也不同,但其逻辑层面上,是相同的,那我们就可以把相同的逻辑关系抽象出来固化封装。

#include <stdint.h>

/* 解释器的实现 */
void Interpreter(uint8_t tri_cond,
				uint8_t time,
				void (*func)(void *),
				void *pt,
				uint8_t *tmr_cnt)
{
    
    
	if (tri_cond)
	{
    
    
		if (*tmr_cnt >= time)
		{
    
    
			*tmr_cnt = 0;
			func(pt);
		}
	}
	else
	{
    
    
		*tmr_cnt = 0;
	}
}

extern uint8_t KeySta[2];

uint8_t Cnt[2] = 0;

/* 中断里调用,1s调用一次 */
void TmrTick(void)
{
    
    
	for (uint8_t i = 0; i < 2; i++)
	{
    
    
		Cnt[i]++;
		if  (Cnt[i] > 200)
		{
    
    
			Cnt[i] = 200;
		}
	}
}


/* 用户操作 */
void UserFunc1(void *pt)
{
    
    
	A += 1;
}

void UserFunc2(void *pt)
{
    
    
	A -= 1;
}

int main(void)
{
    
    
	Interpreter((1 == KeySta[0]),
				3,
				UserFunc1,
				NULL,
				&Cnt[0]);
				
	Interpreter((1 == KeySta[1]),
				2,
				UserFunc2,
				NULL,
				&Cnt[1]);

	return 0;
}

    解释器一般可以跟表驱动很好的贴合使用,再加上表驱动,来看下上面代码的实现。

#include <stdint.h>

/* 解释器传参封装 */
struct tagInterpreterPara
{
    
    
	uint8_t TriCond;
	uint8_t Time;
	void (*Func)(void *);
	void *Pt;
	uint8_t TmrCnt;
};

/* 解释器的实现 */
void Interpreter(struct tagInterpreterPara *table)
{
    
    
	if (table->TriCond)
	{
    
    
		if (table->TmrCnt >= table->Time)
		{
    
    
			table->TmrCnt = 0;
			table->func(table->pt);
		}
	}
	else
	{
    
    
		table->TmrCnt = 0;
	}
}

/********************************后续变更需要更改的地方*********************************/
extern uint8_t KeySta[2];

/* 用户操作 */
void UserFunc1(void *pt)
{
    
    
	A += 1;
}

void UserFunc2(void *pt)
{
    
    
	A -= 1;
}

/* 表 */
struct tagInterpreterPara UserTable[] =
{
    
    
	{
    
    KeySta[0], 3, UserFunc1, NULL, 0},
	{
    
    KeySta[1], 2, UserFunc2, NULL, 0},
};
/***************************************************************************************/

/* 中断里调用,1s调用一次 */
void TmrTick(void)
{
    
    
	for (uint8_t i = 0; i < sizeof(UserTable)/sizeof(UserTable[0]); i++)
	{
    
    
		UserTable[i].TmrCnt++;
		if  (UserTable[i].TmrCnt > 200)
		{
    
    
			UserTable[i].TmrCnt = 200;
		}
	}
}

int main(void)
{
    
    
	for (uint8_t i = 0; i < sizeof(UserTable)/sizeof(UserTable[0]); i++)
	{
    
    
		Interpreter(&UserTable[i]);
	}

	return 0;
}

适用范围

  1. 对于有固定行为模式,并且同样的行为有大量重复出现的场景下可以使用。

优势

  1. 对于有特定行为模式的语句,可以最大程度地减少重复性动作的编写。

劣势

  1. 行为模式完全固化,当需要有新的行为变化时,无法扩展。
  2. 转换后的语言可能跟原本的通用语言存在冲突,容易造成应用混乱。

猜你喜欢

转载自blog.csdn.net/u012749085/article/details/126830732