【阅读笔记】理解表驱动设计

前言

《代码大全2》中单独罗列了一章描述表驱动法,旨在告诉读者,面对复杂的多分支结构(if 或者是 case),可以想着使用表驱动法实现或者改造。面向对象的多态也是用来改善多 if 或者多 case 的代码结构。诚然,多if 或者多 case 的代码并非十恶不赦,但是也需要看到多态实现和表驱动实现的好处,用于评估改造的成本和收益。工作中曾经也用过类似的思想,这里记录下来。


问题定义

  • 存在多分支结构
// 判断一个月有多少天
if (month = 1) {
	days = 31
} else if (month = 2) {
	days = 28
} else if (month = 3) {
	days = 31
} ..... else {

}
  • 嵌套 if 结构,用于支撑复杂逻辑
if (女性) {
	if (单身) {
		if (不抽烟) {
			if (年龄 < 18) {
				保险费率 = 200
			} else if (年龄 < 38) {
				保险费率 = 300
			} ... 
		}
	}
}
  • 解析多种数据格式(规则)。这里也可以使用多态改造,后续会交代表驱动的实现为什么比多态更好
if (规则1) {
	规则1解析逻辑块 
} else if (规则2) {
	规则2解析逻辑块 
} else if ...  {
	
}

需求定义

改造代码属于非功能性需求,是优化程序的,让复杂逻辑保持迭代健康的手段。主要的目的是

  • 为新需求奠定良好的实现基础
  • 明确程序职责,把变化的东西归集到表中,提高拓展性

改造过程

  • 存在多分支结构
// 判断一个月有多少天
if (month = 1) {
	days = 31
} else if (month = 2) {
	days = 28
} else if (month = 3) {
	days = 31
} ..... else {

}

这个场景很简单,用一个数组维护即可

int daysPerMonth[] = {0, 31, 28,31, ....}
days = daysPerMonth[input]
  • 嵌套 if 结构
if (女性) {
	if (单身) {
		if (不抽烟) {
			if (年龄 < 18) {
				保险费率 = 200
			} else if (年龄 < 38) {
				保险费率 = 300
			} ... 
		}
	}
}

不管逻辑再怎么复杂,都能把嵌套的逻辑扁平化,如:

性别 - 是否单身 - 是否抽烟 - 年龄 - 费率

形如数据库中的一条表字段,那么就能启示我们这么做:

保险费率 = rateTable(性别, 是否单身, 是否抽烟, 年龄)
  • 解析多种数据格式(规则)
if (规则1) {
	规则1解析逻辑块 
} else if (规则2) {
	规则2解析逻辑块 
} else if ...  {
	
}

规则是一种较为复杂的实现,不同的规则可能需要读取不同的字段。简单来说,不同规则所关注的信息类型、范围可能不同。那么针对这些容易变化的场景,表驱动大有用处。

// 规则定义Eg
规则1 
	总字段数 3
		 [FiledType]	[FiledValue]
	字段1 角色 			admin
	字段2 姓名 			james
	字段3 参数 			{a: hello}
规则1结束

// 有多个规则,都放到数组中
fieldDescription[]

// 规则字段对象表定义 AbstractFiled 是一个抽象类 (这里引入了多态)
AbstractFiled field[]
field[角色] = new 角色解析器 extends AbstractFiled ();
field[姓名] = new 姓名解析器 extends AbstractFiled ();
field[参数] = new 参数解析器 extends AbstractFiled ();


// 规则解析核心代码
while (有待消费的规则事件) {
	规则k = fieldDescription[规则事件.规则编号]
	n = 1
	while (未遍历完规则k的所有字段) {
		fieldType = 规则k[字段n].FiledType
		fieldValue = 规则k[字段n].FiledValue
		解析器 = field[fieldType]
		解析器.解析(fieldType, fieldValue)
		n++
	}
}

可以看到,用了表驱动设计,核心代码块很好看懂,变化的部分都被封装到了不同的类去了。
其中fieldDescription[规则事件.规则编号] 规则k[字段n] field[fieldType] 都是查表的思想


表驱动设计中数据范围的处理

上文提到的查询都可以视为 索引表, 但是索引是一个区间的时候该如何映射到入参呢,《代码大全2》把以下这种解决方案成为 阶梯访问表

  • 一个简单的例子
[分数 / 总分 定义]		[等级]
>= 90% 					A
< 90% 					B
< 75% 					C
< 65%					D
< 50%					F

根据分数查等级的SQL

select 
	等级 
fromwhere 
	自己的分数 / 总分 >= [分数 / 总分 定义]
limit 1	
  • 同理,更加复杂的例子也可以用这个方法
概率			保险索赔额度
0.45232			0
0.54723			254
0.5323			43535

后记

曾经在学习 Spock 测试框架的时候就了解到了表驱动设计。遗留的问题今天终于理解了。包括工作中遇到的异常节点的规则配置、lookup大宽表、角色权限表查询、多条件计算器实现。举的例子跟公司业务相关就不太好展开描述。简单来说,印证了表驱动设计有足够多的应用场景。使用表驱动设计的时候,想一想能不能用多态,如果代价合理,引入多态是个不错的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值