几年前,我写了一篇关于如何避免if-else语句序列的文章。 在那篇文章中,我演示了几种选择:
- 正确的OOP设计的使用
- 地图
- 如果没有
return
,请在case
switch
语句。
最近,我偶然发现了一个稍微复杂的用例。 这篇文章对此进行了描述,并详细介绍了Kotlin中可用的其他选项。
模拟一个简单的if ... else序列
让我们从建模简单的if-else语句序列开始。 想象一下,我们需要检查一个字母的值以返回与后者相关的整数。
如前一篇文章所述,if-else的一种简单替代方法是创建一个映射,以字母为键,以整数为值。
valmappings=mapOf(
"A"to1,
"B"to2,
"C"to3,
"D"to4,
"E"to5,
"F"to6,
"G"to7
)
valresult=mappings["A"]
请记住,始终对键使用不可变类型!
使问题更加复杂
现在想象一下,代替检查一个值,需要检查两个不同的输入以计算返回值。 解决此问题的直接方法是使用嵌入式, if
:
if(input1=="A"){
if(input2=="A")return1
if(input2=="B")return2
if(input2=="C")return3
// and the rest
}elseif(input1=="B"){
if(input2=="A")return2
if(input2=="B")return4
if(input2=="C")return6
// and the rest
}
// and the rest
使用地图方法,这类似于:
valmappings=mapOf(
"A"tomapOf(
"A"to1,
"B"to2,
"C"to3
// and the rest
),
"B"tomapOf(
"A"to2,
"B"to4,
"C"to6
// and the rest
)
// and the rest
)
valresult=mappings["A"]["B"]
我相信以前的代码很难阅读:
- 创建地图需要大量的嵌套代码
- 获得结果是一个两步过程-首先获得一维地图,然后获得标量值
- 键的顺序不应混淆:
mappings["A"]["B"]
与(可能)不同于mappings["B"]["A"]
解决复杂的问题
让我们停下来,然后将第一个序列建模为数组:
一个 | 乙 | C | d | Ë | F | G |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
上述解决方案具有一维。 现在想象一下,选择是一个2D矩阵:
一个 | 乙 | C | d | Ë | F | G | |
---|---|---|---|---|---|---|---|
A | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
B | 2 | 4 | 6 | 8 | 10 | 12 | 14 |
C | 3 | 6 | 9 | 12 | 15 | 18 | 21 |
D | 4 | 8 | 12 | 16 | 20 | 24 | 28 |
E | 5 | 10 | 15 | 20 | 25 | 30 | 35 |
F | 6 | 12 | 18 | 24 | 30 | 36 | 42 |
G | 7 | 14 | 21 | 28 | 35 | 42 | 49 |
这类似于多重地图,即具有两个键的地图。 Java和Kotlin都不具有Multimap
类型,但是库却具有:
- 例如, 番石榴里有一个
- 还有另一个在Apache Commons Lang中
由于使用了Pair
和Triple
类,因此使用Kotlin不需要多达3个尺寸的多图。
valmappings=mapOf(
("A"to"A")to1,
("A"to"B")to2,
("A"to"C")to3,
("B"to"A")to2,
("B"to"B")to4,
("B"to"C")to6
// and the rest
)
valresult=mappings["A"to"B"]
对于尺寸大于3的键,请创建专用的不可变类型,并覆盖其equals()和hashCode()方法。
返回计算
在旧的帖子中,当代码没有返回结果而是调用了一个方法时,使用了一个switch
:
when(key){
"A"to"A"->doSomething()
"A"to"B"->doSomethingElse()
"A"to"C"->andNowForSomethingCompletelyDifferent()
}
但是,将计算存储在地图中是更好的选择。 然后可以动态调用它:
valmappings=mapOf(
("A"to"A")to{doSomething()},
("A"to"B")to{doSomethingElse()}
("A"to"C")to{andNowForSomethingCompletelyDifferent()}
)
mappings["A"to"B"]?.invoke()
唯一的要求是所有计算都具有相同的签名:这里是一个不带输入参数的函数。
上面的代码中未使用返回值。 如果是这样,可以很容易地捕获它:
val result = mappings["A" to "B"]?.invoke()
如果在没有键匹配时需要默认值,则getOrDefault()
函数是您的朋友:
valmappings=mapOf(
("A"to"A")to{doSomething()},
("A"to"B")to{doSomethingElse()}
("A"to"C")to{andNowForSomethingCompletelyDifferent()}
)
valresult=mappings.getOrDefault("A"to"E"){doDefault()}.invoke()
结论
在较早的文章中,我列出了一些简单的技术来避免与简单用例相关的if-else语句序列。 在本教程中,我展示了更先进的技术来满足更高级的用法:用于嵌套求值的多图,并在需要执行代码时将计算结果作为值存储在图中。
编写代码时,提高代码的可读性应该是第一要务。 它不需要花哨的技术,只需不断地努力就可以实现它。 认识和理解自己的语言以及可用的库对于实现该目标大有帮助。
翻译自: https://blog.frankel.ch/even-more-readable-code-without-if-else/