当前位置: 首页 > news >正文

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

目录

    • 前言
    • 问题定义
    • 需求定义
    • 改造过程
    • 表驱动设计中数据范围的处理
    • 后记

前言

《代码大全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大宽表、角色权限表查询、多条件计算器实现。举的例子跟公司业务相关就不太好展开描述。简单来说,印证了表驱动设计有足够多的应用场景。使用表驱动设计的时候,想一想能不能用多态,如果代价合理,引入多态是个不错的选择。

相关文章:

  • [C语言、C++]数据结构作业:用递归实现走迷宫(打印正确通路即可)
  • JVM垃圾回收系列之垃圾收集器二
  • 2023年,前端开发未来可期
  • mysql 常用查询优化策略详解
  • 区块链的认识
  • Kubernetes中的yaml文件
  • Python编程挑战赛
  • Windows下安装RabbitMQ的步骤
  • 欢迎女神科学家颜宁回国,并祝她如愿以偿
  • 学习笔记-IPC$(Internet Process Connection)
  • 第三站:函数(第二幕)
  • 信贷审批中拒绝原因码的分布与监控设计
  • Mybatis之foreach
  • 【JavaEE基础与高级 第60章】Java中的反射获取成员方法Method、获取成员变量Field(下篇)
  • 2023秋招感悟
  • Hadoop学习总结
  • 集合(Set)和有序集合(ZSet)的基本使用方法详解【Redis】
  • 【SpringSecurity】SpringSecurity2.7.x 的使用(05)
  • AVR单片机开发1——IO口的输入和输出
  • 破解系统密码与重装windows系统
  • 建设银行南昌分行引金融“活水”,精准灌溉乡村沃土
  • 外媒称菲方允许菲官员窜台,国台办:应停止在台湾问题上玩火
  • 日本希望再次租借大熊猫,外交部:双方就相关合作保持密切沟通
  • 商务部新闻发言人就波音公司飞回拟交付飞机答记者问
  • 亮剑浦江丨上海网信部门处罚一批医疗服务类互联网企业,三大类问题值得关注
  • 澎湃思想周报丨数字时代的育儿;凛冬已至好莱坞