不要再用if/else和switch/case了,快使用“表驱动法”代替,面向对象思维,对修改关闭。

Posted CodeBowl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不要再用if/else和switch/case了,快使用“表驱动法”代替,面向对象思维,对修改关闭。相关的知识,希望对你有一定的参考价值。

使用表驱动法,替换复杂的if/else和switch/case语句。

复杂的if/else

忘记从哪里看到这么一段话:

互斥条件表驱动
嵌套条件校验链
短路条件早return
零散条件可组合

在实际编程中,我已经遇到了第一种情况了,就是过多的条件导致的分支。在之前我都会用switch/case代替这种情况,毕竟这就是switch的作用,但是这并不是最优解,因为当条件逐渐复杂到几百条的时候,每次修改都是一种痛苦,过长的代码阅读起来也不方便,并且不满足“面向对象”的思想:对修改关闭,对扩展开放。
所以这时候就需要用到了表驱动法!

表驱动法

表驱动法是什么

表驱动是一种编程模式,是一种将输入变量作为索引在表里查找直接的结果或者处理函数,而不是用很多的逻辑语句来进行判断(比如if-sle||switch_case).索引表可以是个数组、map或者其他高效率查找的数据结构。

举个例子

switch(weeek)

	case Monday:
	Console.WritleLine("星期一");
	case Tuesday:
	Console.WritleLine("星期二");
	case Wednesday:
	Console.WritleLine("星期三");
	case Thursday:
	Console.WritleLine("星期四");
	case Friday:
	Console.WritleLine("星期五");
	case Saturday:
	Console.WritleLine("星期六");
	case Sunday:
	Console.WritleLine("星期日");


这段case语句代码实现的功能是将星期的英文转化为中文输出,幸亏一周只有七天,若有八天、九天还需要进行代码的增加,想想都特别麻烦。当使用表驱动法时,你就能发现当遇到此类时会变得特别方便。

解决方法:
1、case语句或if-else语句的结果返回结果是某个值
其实上述的例子就属于这类情况,表驱动法解决过程是:首先将所有返回的值写进一个数组中,定义数组weekDay

string weekDay[]="星期一","星期二","星期三","星期四","星期五","星期六","星期日"

然后要解决的是将case值和数组下标索引能联系起来,这里最好的办法就是使用枚举型,这是因为枚举值是可以转化为数字的,定义一个枚举Week

public enum Week

  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday


(int)Week.Monday的值是1,如此就解决了将case值和数组下标之间转换的问题。所以上例通过表驱动法来实现就只需要一行代码就可以了:

week=weekDay.Monday;//week为WeekDay中任意一个枚举值,这里设为Monday
string result=weekDay[(int)week];
Console.Writle(result);

实战来了

下面通过加减乘除来实现一个简易计算器,先用未优化的switch-case来实现:

#include <iostream>
#include <time.h>
 
int add(int a, int b)

	return a + b;

int subtraction(int a, int b)

	return a - b;

int multiplication(int a, int b)

	return a * b;

int division(int a, int b)

	assert(b != 0);
	return a / b;

 
int Calculator(char symbol, int a, int b)

	int ans = 0;
	switch (symbol)
	
	case ' + ':
		ans = add(a, b);
		break;
	case ' - ':
		ans = subtraction(a, b);
		break;
	case ' * ':
		ans = multiplication(a, b);
		break;
	case ' / ':
		ans = division(a, b);
		break;
	default:
		std::cout << "wrong symbol" << std::endl;
		break;
	
	return ans;

 
int main()

	int res = 0;
	clock_t startTimes = clock();
    // 一加一减,防止结果溢出
	for (int i = 0; i < 1000000; ++i)
	
		res = Calculator('+', res, i);
		res = Calculator('-', res, i);
	
	clock_t endTimes = clock();
	std::cout << "switch cost times : " << (endTimes -startTimes)/ CLOCKS_PER_SEC << "ms." << std::endl;
	getchar();
	return 0;

接着,我们使用表驱动方式来做优化:

#include <iostream>
#include <time.h>
#include <map>
 
int add(int a, int b)

	return a + b;

int subtraction(int a, int b)

	return a - b;

int multiplication(int a, int b)

	return a * b;

int division(int a, int b)

	assert(b != 0);
	return a / b;

// 定义函数指针
using func = int (*)(int, int);
 
int main()

	int res = 0;
	// 这里也可以用map来代替函数指针数组,但是小数据量上实际效果可能还没switch-case快
	func table[4];
	table['+'] = &add;
	table['-'] = &subtraction;
	table['*'] = &multiplication;
	table['/'] = &division;
	clock_t startTimes = clock();
	for (int i = 0; i < 1000000; ++i)
	
		res = table['+'](res, i);
		res = table['-'](res, i);
	
	clock_t endTimes = clock();
	std::cout << "arrary table cost times : " << (endTimes - startTimes) / CLOCKS_PER_SEC << "ms." << std::endl;
	getchar();
	return 0;

总结

表驱动方式代码量少,逻辑清晰,更符合设计模式思想,方便函数增添或者删除。但是在c++里需要用到函数指针、指针数组,对新手不友好。但是如果采用合理的索引表,在数据量较大的情况下可以有效提升程序运行速度。

参考资料

https://blog.csdn.net/qq525003138/article/details/121374565
http://www.cppcns.com/ruanjian/c/418836.html
我有N种方法消除 if-else,而你却无可奈何(第二篇:表驱动编程方法)

以上是关于不要再用if/else和switch/case了,快使用“表驱动法”代替,面向对象思维,对修改关闭。的主要内容,如果未能解决你的问题,请参考以下文章

if else和switch case那个效率更高一点

switch case vs if else [重复]

if...else与switch...case的执行效率问题

switch...case 与 if...else 的性能分析

分支语句 if...else switch..case...break

switch 和 if else if else 有什么区别