表驱动法,逻辑控制优化利器
hello,大家好,我是张张,「架构精进之路」公号作者。
最近好多同学在开发过程中谈到设计表结构的一些idea,为了让大家少走一些弯路,今天就计划聊聊表驱动法吧~
1、概念介绍
表驱动法 是一种编程模式,从表里查找信息而不使用逻辑语句(if/else)
事实上,凡是能通过逻辑语句来选择的事物,都可以通过查表来选择。
对简单的情况而言,使用逻辑语句更为容易和直白,但随着逻辑链的越来越复杂,查表法也就愈发显得更具有吸引力。
应用原则
适当的情况下,采用表驱动法,所生成的代码会比复杂的逻辑代码更简单,更容易修改,而且效率更高。
2、应用实践
2.1 直接访问
2.1.1 今天周几?
传统写法:
String today = "周日";
Switch( dayForMonth % 7 ){
case 0 :
today = "周日";
case 1 :
today = "周一";
case 2 :
today = "周二";
case 3 :
today = "周三";
case 4 :
today = "周四";
case 5 :
today = "周五";
default:
today = "周六";
}
表驱动法:
2.1.2 每个月多少天?
传统写法:
if(1 == iMonth) {
iDays = 31;
} else if(2 == iMonth) {
iDays = 28;
} else if(3 == iMonth) {
iDays = 31;
} else if(4 == iMonth) {
iDays = 30;
} else if(5 == iMonth) {
iDays = 31;
} else if(6 == iMonth) {
iDays = 30;
} else if(7 == iMonth) {
iDays = 31;
} else if(8 == iMonth) {
iDays = 31;
} else if(9 == iMonth) {
iDays = 30;
} else if(10 == iMonth) {
iDays = 31;
} else if(11 == iMonth) {
iDays = 30;
} else if(12 == iMonth) {
iDays = 31;
}
表驱动法:
把逻辑写成 map 或是 list,一目了然,可以搞个2维数组还加上了闰年的逻辑。
const monthDays = [
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
]
function getMonthDays(month, year) {
let isLeapYear = (year % 4 === 0) && (year % 100 !== 0 || year % 400 === 0) ? 1 : 0
return monthDays[isLeapYear][(month - 1)];
}
console.log(getMonthDays(2, 2000))
2.2 索引访问
有时只用一个简单的数学运算还无法把 age 这样的数据转换成为表键值,这种情况可以通过索引访问的方法加以解决。
索引应用:先用一个基本类型的数据从一张索引表中查出一个键值,然后在用这一键值查出需要的主数据。
举例:
有100件商品,商店物品编号(范围 0000-9999)
创建两张表:索引表(0-9999),物品(查询)表(0-100)
索引访问有两个优点:
如果主查询表的每条记录都很大,那创建一个浪费了很多空间的数组所用的空间,要比建立主查询表所用的空间小得多。
操作索引中的记录比操作主查询表的的记录更方便,编写到表里面的数据比嵌入代码的数据更容易维护。
2.3 阶梯访问
这种访问方法不像索引结构那样直接,但是它要比索引访问方法节省空间。
阶梯结构的基本思想:表中的记录对于不同数据范围有效,而不是对不同的数据点有效。
举例:
一个等级评定的应用程序,其中“B”记录所对应的范围是 75.0%-90.0%
>= 90.0% A
<90.0% B
<75.0% C
<65.0% D
<50.0% F
这种划分范围用在查询表中是不合适的,因为你不能用简单的数据转换函数来把表键值转换成 A-F 字母所代表的等级。用索引也不合适,因为这里用的是浮点数。
在应用阶梯方法的时候,必须谨慎的处理范围的端点。
Dim rangeLimit() As Double = {50.0, 65.0, 75.0, 90.0, 100.0}
Dim grade() As String={"F", "D", "C", "B", "A"}
maxGradeLevel = grade.Length - 1
// assign a grad to a student based on the student's score
gradeLevel= 0
studentGrade = ”A"
while( studentGrade = "A" and gradeLevel < maxGradeLevel )
if( studentScore < rangeLimit( gradeLevel ) ) then
studentGrade = grade ( gradeLevel)
end if
gradeLevel = gradeLevel + 1
wend
与其他表驱动法相比,这种方法的优点在于它很适合处理那些无规则的数据。
在使用阶梯访问时需要注意的一些细节:
1)留心边界端点
注意边界:< 与 <=,确认循环能够在找出最高一级区间后恰当地终止。
2)考虑用二分查找取代顺序查找
如果列表很大,可以把它替换成一个准二分查找法,从头查找是很耗费性能的
3)考虑用索引访问来取代阶梯访问
阶梯访问中的查找操作可能会比较耗时,如果执行速度很重要,那可以考虑用索引访问来取代阶梯查找,即以牺牲存储空间来换取速度。
2.4 构造查询键值
如上述例子,我们希望能够将数据作为键值直接访问表,这样既简单又快速。
但是问题或者数据通常并不是这样友好,那就需要引出 构造查询键值 的方法。
费率与年龄、性别、婚姻及交费年数等不同情况而变动。
1)复制信息从而能够直接使用键值
age补齐:50 岁以上的年龄都复制一份 50 岁的费率。
这样优点在于表自身结构非常简单那,访问表的逻辑也很简单;
缺点在于复制生成的冗余信息会浪费空间,也即是利用空间换效率。
2)转换键值以使其能够直接使用
费率表查询时,用一个函数将 age 转换为另一个数值。
在此例子中,该函数必须把所有介于 0-5 直接的年龄转换成一个键值,例如 5,同时把所有超过 50 的年龄都转换成另一个键值,例如 50。
这样在检索前可以用 min()和 max()函数来做这一转换。
例如,你可以用下述表达式:max(min(50, age), 17) 来生成一个介于 17-50 之间的表键值。
3)把键值转换提取城独立子程序
如果你必须要构造一些数据来让它们像表键值一样使用,那就把数据到键值的转换操作提取成独立的子程序。这样可避免在不同位置执行了不同的转换,也使得转换操作修改起来更加容易。
任务是个方法,不再是数值了,这里我们可以利用 Dart 这样的支持高阶函数的语言特性,把方法当做一个对象存储在表中。
var data = <String, Map>{
"A": {
"name": "AA",
"action": (name) => print(name + "/AA"),
},
"B": {
"name": "BB",
"action": (name) => print(name + "/BB"),
},
};
var action = data["A"]["action"];
action("kk");
3、总结
1)如何从表中查数据?
List item
直接访问
索引访问
阶梯访问
2)在表里存些什么?
数据
动作(action)-描述该动作的代码/该动作的子程序的引用。
表驱动法提供了一种复杂的逻辑和继承结构的替换方案。如果你发现自己对某个应用程序的逻辑或者继承关系感到困惑,那是否可以通过一个查询表来加以简化。
使用表的关键决策是决定如何去访问表,可以采取直接访问、索引访问或阶梯访问
使用表的另一项关键决策是决定如何去把什么内容放入表中
需要保存浮点数和范围数时,使用阶梯访问的形式。
Thanks for reading!
关注公众号,免费领学习资料
如果您觉得还不错,欢迎关注和转发~
本文分享自微信公众号 - 架构精进之路(jiagou_jingjin)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
腾讯云消息队列TDMQ RabbitMQ 版开启公测,文末有惊喜!
导语 1月6日,TDMQ RabbitMQ 版正式公测!TDMQ RabbitMQ 版是TDMQ系列产品中的一款子产品,是一款分布式高可用的消息队列服务,支持AMQP 0-9-1 协议,完全兼容开源 RabbitMQ 的各个组件与概念。欢迎大家扫描文末二维码使用体验! TDMQ RabbitMQ 版的背景 众所周知,RabbitMQ是一个历史比较悠久的消息队列中间件,它是使用Erlang语言开发的实现AMQP(Advanced Message Queue Protocol 高级消息队列协议)的消息中间件。RabbitMQ最初起源于金融系统,它在可靠性、可用性、扩展性、消息持久化、高并发等方面的有着卓越的表现。TDMQ RabbitMQ 版是依托于TDMQ而生的一款子产品,支持AMQP 0-9-1 协议,完全兼容开源 RabbitMQ 的各个组件与概念,稳定可靠,易用免运维,相比开源RabbitMQ,性能更佳且易拓展。 产品介绍 TDMQ RabbitMQ 版(TDMQ for RabbitMQ)具备计算存储分离,灵活扩缩容的底层优势,拥有极为灵活的路由来适应各类业务的消息投递规则,能有...
- 下一篇
Tengine + BabaSSL ,让国密更易用!
文|杨洋(花名:凯申 ) 蚂蚁集团高级技术专家 负责密码学工程能力建设、BabaSSL 开源社区建设 本文 2366 字 阅读 5 分钟 近日,国内著名 Web 服务器和反向代理开源软件 TengineBabaSSL 完成了对 BabaSSL的适配工作。 Tengine 对 BabaSSL 提供的特殊 API 进行了适配,并增加对 NTLS 相关能力的支持。 「详细 Pull Request 请见」:https://github.com/alibaba/tengine/pull/1595 至此,对我国密码行业相关安全通信协议,有使用需求的用户可以直接使用 Tengine + BabaSSL 的组合。而无需额外的 patch 或者代码改动,从用户使用的角度进一步提升了便利性。 PART. 1 NTLS 目前,我国密码行业中有两个主要的通信协议相关的技术标准。一个是由信安标委于 2020 年发布的 TLCP 协议,即传输层密码协议;另外一个则是由密标委在 2012 年发布的 GM/T 0024《SSL VPN 技术规范》(以下简称 0024)。 TLCP 和 0024 的具体内容差别不大,...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装