如何删掉编程中的 Switch 语句

目录
  • 什么是多重方法?
  • 多重方法是什么样子?
  • 多重方法有什么好处?
  • 实现一个多重方法库

多重方法是一种有趣的方式,可以帮你摆脱令人讨厌的 switch。而且,这也有助于提升代码的可读性。所以,在决定继续坚持使用 switch 之前,一定要先试一试。

本文最初发布于 Bits and Pieces。

很多开发者都讨厌switch语句,包括我。并不是因为这个语句没用,也不是因为它太难了。

理解switch语句的工作原理非常简单,问题是当你真的遇到它时,就必须停下手头的一切工作,集中精力阅读它,以确保不会遗漏任何东西,比如,缺少break语句可能会导致一些意想不到的行为,或者一个case中大约有 20 行代码。

关键是,原谅我使用一个花哨的术语:理解switch语句(在现实世界中)所需要的认知负荷相当重。我相信,作为开发人员,我们的目标是编写方便人类阅读的代码。在这方面,这个语句提供不了什么帮助。

但是,我写这篇文章不是为了对它进行抨击,我是要向你(之前也包括我)展示三个关于如何避免使用switch语句的示例,让我们来看一种函数式编程技术:多重方法。

什么是多重方法?

我第一次听到这个词,还是在播客“20 MinJS”中采访 Yehonathan Sharvit 时。当时的采访是关于他即将由 Manning 出版的著作《面向数据的编程》。

他提出这一概念是为了从功能上取代继承,这无疑是可行的。在这个过程中,他展示了switch语句是如何被取代的。因此,让我们暂时把 OOP 放在一边,只关注第二部分:消除代码中丑陋的switch

什么是多重方法?它只是一个能够根据接收到的参数选择最佳实现的函数。换句话说,想象一下,如果你把丑陋的switch语句放在函数中,然后对所有人隐藏实现。

唯一的区别是,你的解决方案只适用于一个函数。今天我们将讨论如何在运行中生成多个多重方法。

多重方法是什么样子?

当然,每种语言都有自己的变体,但我今天主要讲 JavaScript。

在这种语言中,多重方法的使用方法如下:

//我们将使用的数据
const myDog = {
    type: "dog",
    name:"Robert"
}
const myCat = {
    type: "cat",
    name: "Steffan"
}
//自定义函数实现
function greetDogs (dog) {
    console.log("Hello dear Dog, how are you today", dog.name, "?")
}
function greetCats(cat) {
    console.log("What's up", cat.name, "?")
}
//定义我们的多重方法
let greeter = null
greeter = multi(
    animal => animal.type,
    method("dog", greetDogs),
    method("cat", greetCats)
)(greeter)
// 调用多重方法
greeter(myDog)
greeter(myCat)

这个例子做了很多事,让我来说明下:

  • 我定义了 2 个对象myCatmyDog,我将把它们作为参数,多重方法将根据它们确定自己的行为。
  • 我定义了 2 个自定义函数greetDogsgreetCats,它们的实现稍有不同。它们将代表switch中每个case语句里的代码。
  • 然后我调用一些函数,尤其是multimethod,来定义多重方法greetermulti函数接收 3 个属性:一个分配器(dispatcher),我们将用它返回的值来确定要执行的逻辑片段;还有两个方法,分别代表switch的一个case语句。请注意,每次调用method时,要首先指定触发第二个参数的值(这是实际的逻辑所在)。
  • 最后,我使用同一个函数(我的多重方法)来执行两个不同的逻辑片段,而不需要在任何地方使用switchif语句。

多重方法有什么好处?

当然,我们在这里没有施展任何类型的魔法,我们只是重写了决策逻辑的表达方式,类似下面这样的switch语句:

switch(animal.type) {

  case "dog":
    greetDogs(animal);
  break;
  case "cat":
    greetCats(animal);
 break;
}

那么,如果我们可以直接这样做,为什么还要大费周章地使用多重方法呢?问题的关键是可读性。

switch语句非常开放,显示了我们的决策逻辑的实现。换句话说,这个语句是命令式的。它向你展示了决策树的内部运作情况,这意味着阅读代码的人将不得不在头脑中解析代码。因此,我们又回到了认知负荷的概念。这使得开发者要阅读并在头脑中解析代码。

你要知道,大多数开发人员在遇到像上面这样的switch时,不会有什么反应。但是,这也不是一个实际的例子。通常情况下,case语句包含的代码更多,也更难阅读。

而多重方法隐藏了决策逻辑的内部结构,你所知道的只是你对它做了设置,它将以某种方式工作。你更关心的是功能而不是实际的实现。这被称为“声明式编程”,有助于提高代码的可读性,同时降低开发人员的认知负担。这是因为它在逻辑上增加了一层抽象,为我们提供了更接近人类语言的表达工具。

如果这还不能说服你,还有一个优点:可扩展性。

如果你需要在switch中添加另一个选项,就必须回到代码中修改同一个switch,如果你,比如说,碰巧忘记添加break语句,就有可能造成问题,就像下面这样:

switch(animal.type) {
  case "rabbit":
    greetRabbits(animal);
  case "dog":
    greetDogs(animal);
  break;
  case "cat":
    greetCats(animal);
 break;
}

还是个非常简单的例子,但如果是真实世界中一段更长的代码,那么这种情况出现的几率就更大了。

以防你对这种行为不熟悉,请让我做个说明。第一个case中缺失break,会导致在动物类型为“rabbit”时也执行第二个case下的逻辑。

然而,有了多重方法,我们就可以不断地根据需要对它进行扩展:

let extendedGreeter = multi(
    animal => animal.type,
    method("parrot", sayHiParrot)
)(greeter)

现在,这个新方法extendedGreeter对“dog”、“cat“、”parrot“就都有效了,而我们不必再回去修改已有的代码。

这是一个很大的好处,因为我们都知道,每次我们触碰可以正常工作的代码时,都有一点可能引入 Bug。在这里,我们把可能性降低到 0。

实现一个多重方法库

首先,你要知道,已经有一些库在处理这个问题了,其中一个例子是@arrows/multimethod

尽管如此,对这些实现进行逆向工程总是很有趣,所以让我们看一看如何实现一个基本的多重方法库,以适应到目前为止所展示的例子。

理解这个问题的关键是,我们需要一个分配器函数来给提供一个实际的值,我们将用它作为判断执行哪个方法的键。而且,我们不能对switch语句进行硬编码,因为选项的数量是不固定的。

不能光说不练,下面是实现:

function method(value, fn) {
    return {value, fn}
}
function multi(dispatcher, ...methods) {
    return (originalFn) => {
        return (elem) => {
            let key = dispatcher(elem)
            let method = methods.find( m => m.value === key)
            if(!method) {
                if(originalFn) {
                    return originalFn(elem)
                } else {
                    throw new Error("No sure what to do with this option!")
                }
            }
            return method.fn(elem)
        }
    }
}

method函数只是把键和实际的逻辑耦合在一起,没有别的。multi函数中的代码才有趣,它返回一个匿名函数,以原始函数为参数并返回一个新函数,后者根据分配器代码(我们的第一个参数)返回的值执行不同的东西。

让我们逐行看下:

  • 首先,调用第 8 行的函数时提供一个属性(比方说myDog)。
  • 第 9 行的分配器逻辑会获取myDog并返回其类型,即“dog”。
  • 然后在第 10 行,我们找到第一个与该类型匹配的方法。
  • 如果没有方法匹配,但我们有一个有效的“originalFn”(也就是说,我们正在扩展一个原始的多重方法),我们会让它来处理这种情况。否则,我们将抛出一个异常,因为我们对此无能为力。
  • 然而,如果找到了匹配的方法,就在第 18 行执行它,并将原始属性“myDog”传递给它。

就是这样。没那么复杂,对吗?当然,如果你想提供“默认”情况处理而不是抛出一个异常,或者你想处理多属性决策(比如根据属性typename决定逻辑,而不是只根据第一个属性),就得编写更多的代码了。

不过,还是那句话,如果你打算使用多重方法,建议你使用一个现有的库,而不是自己去实现。

多重方法是一种有趣的方式,可以帮你摆脱令人讨厌的switch。而且,这也有助于提升代码的可读性。所以,既然你已经了解了多重方法,那么在决定继续坚持使用switch之前,一定要先试一试。

到此这篇关于如何删掉编程中的 Switch 语句的文章就介绍到这了,更多相关Switch 语句删掉内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言中switch语句基本用法实例

    目录 switch语句: switch语句的基本格式 C语言switch语句用法 补充:用switch来给成绩等级 总结 switch语句: 实际生活中,需要做出很多选择,大家都知道做选择可以使用if语句,但是如果选择太多,if语句使用起来就会很繁琐,这个时候就需要一个能将代码简化的语句,也就是我们今天的主角switch语句. switch语句是一个多分支选择语句,并且可以支持嵌套. switch语句的基本格式 switch(表达式){case 常量1:语句1case 常量2:语句2defaul

  • C语言入门篇--学习选择,if,switch语句以及代码块

    目录 1.什么是语句 2.选择语句(分支语句) 2.1if语句&注意事项 2.1.1语法结构 2.1.2悬空else 2.2switch语句&注意事项 2.2.1语法结构 2.2.2switch语句中的break 2.2.3switch语句中的default语句 2.2.4switch语句的嵌套 3.代码块 1.什么是语句 语句:C语言中由一个 ; 隔开的就是一条语句. 例如: int a = 10; printf("haha\n"); 10; 'A'; ; //空语句

  • C语言switch语句详解

    目录 switch 分支结构 快速入门 代码演示 switch 细节讨论 switch 和 if 的比较 案例: 总结 switch 分支结构 快速入门 案例: 请编写一个程序,该程序可以接收一个字符,比如: a,b,c,d,e,f,g a 表示星期一,b 表示星期 二 - 根据用户的输入显 示相依的信息.要求使用 switch 语句完成 代码演示 案例: 请编写一个程序,该程序可以接收一个字符,比如: a,b,c,d,e,f,g a 表示星期一,b 表示星期 二 - 根据用户的输入显 示相依的

  • C语言示例讲解switch分支语句的用法

    目录 1.了解switch分支语句 2.示例 3.default子句 4.练习 1.了解switch分支语句 switch语句也是一种分支语句,常常用于多分支的情况. 比如: 输入1,就会输出星期一 输入2,就会输出星期二 … 输入7,就会输出星期七 对于这样的代码需求,如果用if .else if.else的形式就会显得太复杂.如何用switch语句实现该功能呢?switch语句的结构又是怎样的呢? switch(整型表达式){    语句项;} 其中,整型表达式必须为整型,且为常量(字符也可

  • C语言switch语句和if用法介绍

    目录 switch语句 switch和if的区别 1.switch语句结构示意图 2.if语句结构示意图 switch的高级(灵活)运用 switch的嵌套实现 循环及循环的两个关键字 什么是循环结构 break关键字 continue关键字 switch语句 我们先了解switch是什么,翻译过来的中文意思是开关,在C语言中是指选择不同的开关即不同的分支来完成语句.接下来讲一下switch的结构和基本使用. 一般的switch语句的结构 switch(n){ case 1: 语句1: brea

  • 如何删掉编程中的 Switch 语句

    目录 什么是多重方法? 多重方法是什么样子? 多重方法有什么好处? 实现一个多重方法库 多重方法是一种有趣的方式,可以帮你摆脱令人讨厌的 switch.而且,这也有助于提升代码的可读性.所以,在决定继续坚持使用 switch 之前,一定要先试一试. 本文最初发布于 Bits and Pieces. 很多开发者都讨厌switch语句,包括我.并不是因为这个语句没用,也不是因为它太难了. 理解switch语句的工作原理非常简单,问题是当你真的遇到它时,就必须停下手头的一切工作,集中精力阅读它,以确保

  • Swift编程中的switch...case语句实例解析

    Swift中的switch...case语句可以判断对象类型, Objective-C中则必须是整数. 不可以穿透,可以不写break, var rank = "A" switch rank{ case "A": //相当于if print("优") case "B": // 相当于else if print("优") case "C": // 相当于else if print(&quo

  • Swift中swift中的switch 语句

    废话不多说了,直接给大家贴代码了,具体代码如下所示: /** switch 语句 */ let str = "aAbBacdef" let str2 = "aAbBadef" let str3 = "aAbBadeff" // var array = []; for c in ["A", "a", str3] { switch c { // case "a": case "a&

  • shell编程中for循环语句的实现过程及案例

    目录 前言 示例1:批量添加用户 示例2:根据IP地址检查主机状态 示例3:自动生成一个8位随机密码 取值范围大小写字母和数字 编写脚本应该注意的事项: 总结 前言 本篇是shell编程for循环的实现过程以及一些案例 什么是for循环 读取不同的变量值,用来逐个执行同一组命令 for 变量名 in 取值列表do    命令序列done#用变量名去取值,然后挨个执行命令,其实就是一个遍历的过程 循环体中三种专业的话术:遍历.迭代.递归 [root@bogon ~]# for i in {1..1

  • PHP中的switch语句的用法实例详解

    switch是一个开关语句,那么很多朋友都只知道简单的switch开关语句的用法了,下面一聚教程小编就为各位详细的介绍一下switch用法例子吧. 只所以称为"高级"用法,是因为我连switch的最基础的用法都还没有掌握,so,接下来讲的其实还是它的基础用法! switch 语句和具有同样表达式的一系列的 IF 语句相似.很多场合下需要把同一个变量(或表达式)与很多不同的值比较,并根据它等于哪个值来执行不同的代码.这正是 switch 语句的用途. 注意: 注意和其它语言不同,cont

  • C++中的Switch 语句详情

    在日常的开发当中,我们经常会遇到一种情况,我们用一个变量表示状态.比如关闭-激活-完成,当我们需要判断状态的时候,就需要罗列if-else语句. if (status == 'closed') { // todo }else if (status == 'activated') { // todo }else if (status == 'done') { // todo } 如果只有少数几个还好,当我们要枚举的状态多了之后,写if-else就会非常繁琐.所以C++当中提供了switch语句来代

  • 详解Java编程中if...else语句的嵌套写法

    if...else if...else语句 if语句后面可以跟elseif-else语句,这种语句可以检测到多种可能的情况. 使用if,else if,else语句的时候,需要注意下面几点: if语句至多有1个else语句,else语句在所有的elseif语句之后. If语句可以有若干个elseif语句,它们必须在else语句之前. 一旦其中一个else if语句检测为true,其他的else if以及else语句都将跳过执行. 语法 if...else语法格式如下: if(布尔表达式 1){

  • 浅谈Swift编程中switch与fallthrough语句的使用

    在 Swift 中的 switch 语句,只要第一个匹配的情况(case) 完成执行,而不是通过随后的情况(case)的底部,如它在 C 和 C++ 编程语言中的那样.以下是 C 和 C++ 的 switch 语句的通用语法: 复制代码 代码如下: switch(expression){    case constant-expression  :       statement(s);       break; /* optional */    case constant-expressio

  • 深入剖析Go语言编程中switch语句的使用

    switch语句可以让一个变量对反对值的列表平等进行测试.每个值被称为一个的情况(case),变量被接通检查每个开关盒(switch case). 在Go编程,switch有两种类型. 表达式Switch - 在表达式switch,case包含相比较,switch表达式的值. 类型Switch - 在这类型switch,此时含有进行比较特殊注明开关表达式的类型. 表达式Switch 在Go编程语言中表达switch语句的语法如下: 复制代码 代码如下: switch(boolean-expres

  • php中switch语句用法详解

    本文介绍php中的switch语句的用法,它跟其他语句中的switch用法差不多的,但注意有有一个break语句. PHP中switch语句的标准语法: switch (expression) { case label1: code to be executed if expression = label1; break; case label2: code to be executed if expression = label2; break; default: code to be exe

随机推荐