编程模式之表驱动

这是一个系列贴,以后都会分享一些编程的技巧,以及思路

编程模式是不分语言的,他是一种思想,思路,更高级的抽象,适用于所有图灵完备的语言

往期帖子

本文使用js来表达

表驱动(Table Driver)

表,在数据线结构中通常是作为映射的一类。通常存储的Key-Value键值对,通过Key来索引Value

表驱动,顾名思义就是用查表的方式来获取数据

各位鱼油在刚学习编程语言的时候应该都会写过一个题目那就是这样的

写一个函数,输入分别输入0到6返回相应的星期,比如输入0返回星期日,输入3返回星期三

那个时候通常都会这样写

function week(i) {
     switch (n) {
        case 0:
            return "星期日";
        case 1;
            return "星期一";
        case 2;
            return "星期二";
        case 3:
            return "星期三";
        case 4;
            return "星期四";
        case 5;
            return "星期五";
        case 6;
            return "星期六";
     }
}

或者使用if else if语句来写,使用分支条件来获取数字0到6的映射数据,这样的如果数据一直在添加,条件分支就会越来越长,代码变得非常臃肿,这个时候表驱动的优势则体现里出来

const week = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
function getWeek(n) {
     return week[n]
}

将数据合并成一个数组,将数字的分支条件映射转为对数组下标的索引来获取相应的数据,此时的数组就是一个表,装着数字和数据的对应关系

再来看一题

写一个函数,输入成绩得到评分,90 以上 A ,80 以上 B ,70 以上 C ,60 以上 D ,否则为 E,如输入88,返回B

初学者通常都会这样写

function getGrade(n) {
    if (n > 90) {return "A" } 
    else if (n > 80) {return "B"}
    else if (n > 70) {return "C"}
    else if (n > 60) {return "D"}
    else {return "E"}
}

使用条件分支来确定n是符合评分标准中的哪一个再返回对应的数据

const score = [60, 70, 80, 90, 100]
const grade = ["E", "D", "C", "B", "A"]
function getGrade(n) {
    for (let i = 0; i < score.length; i++) {
        if (n <= score[i]) {
            return grade[i]
        }
    }
    return grade[0]
}

将评分标准和评分分为两个数组也就是两个表,其中两个数组的下标呈关联性,也就是评分标准的下标和评分标准的下标成对应关系,n与评分标准进行判断符合条件后则用评分标准的数据所在的下标对评分进行索引查询到自己想要的数据

这里使用了两个数组,这两个的数组是具有依赖的关联性,所以如果一个修改了另一个也需要修改,多个维护的表增加了一定的维护成本。所以更有经验的开发者会写出以下代码

const table = [
    {score: 60, grade: "E"},
    {score: 70, grade: "D"},
    {score: 80, grade: "C"},
    {score: 90, grade: "B"},
    {score: 100, grade: "A"}
]

function getGrade(n) {
    for (let i = 0; i < table.length; i++) {
        if (n <= table[i].score) {
            return table[i].grade
        }
    }
    return table[0].grade
}

从使用两个数组的下标进行关联变成使用对象的映射进行关联,维护的表更少

还有个更简单的写法

function getGrade(score) {
     const table = [
    {score: 100, grade: "A"},
    {score: 90, grade: "B"},
    {score: 80, grade: "C"},
    {score: 70, grade: "D"},
    {score: 60, grade: "E"},
   ];
   return table.reduce((acc, t) => score <= t.score ? t.grade: acc, "")
}

具体是什么意思就留给鱼油们自己看了

结语

表驱动的编程思维是这样的 -> 数据是易变的,逻辑是稳定的,将数据和逻辑分开处理。

这样做的好处是不会因为数据的改变而逻辑也会改变,也减少了数据和逻辑的耦合性,比如第一个例子,如果改变问题让获取星期一到星期日编程获取一月到十二月,使用表驱动写的代码也只是给将数组里的值更换了而已,查询的逻辑并没有改变。不会因为数据的改变而改变逻辑,也不会因为逻辑的改变而改变数据

表驱动的表达能力不只是像本文中的这些简单的数据处理,就像第二个例子中,表中存放的可以是字符串可以是数字,也可以存放的是函数,也可以是对象。最常见的一个场景就是,需要判断外部输入来调用相应的函数,可以按照第一个例子中的那样,把函数这些数据存到数据结构里,这样就可以把判断逻辑变成对数据结构索引的逻辑了