我有验证,看起来像下面的IP地址一些代码:
sealed abstract class Result
case object Valid extends Result
case class Malformatted(val invalid: Iterable[IpConfig]) extends Result
case class Duplicates(val dups: Iterable[Inet4Address]) extends Result
case class Unavailable(val taken: Iterable[Inet4Address]) extends Result
def result(ipConfigs: Iterable[IpConfig]): Result = {
val invalidIpConfigs: Iterable[IpConfig] =
ipConfigs.filterNot(ipConfig => {
(isValidIpv4(ipConfig.address)
&& isValidIpv4(ipConfig.gateway))
})
if (!invalidIpConfigs.isEmpty) {
Malformatted(invalidIpConfigs)
} else {
val ipv4it: Iterable[Inet4Address] = ipConfigs.map { ipConfig =>
InetAddress.getByName(ipConfig.address).asInstanceOf[Inet4Address]
}
val dups = ipv4it.groupBy(identity).filter(_._2.size != 1).keys
if (!dups.isEmpty) {
Duplicates(dups)
} else {
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
val taken: Iterable[Inet4Address] = ipAvailability.filter(!_._2).keys
if (!taken.isEmpty) {
Unavailable(taken)
} else {
Valid
}
}
}
}
因为它使代码的可读性,我不喜欢嵌套的IFS。 是否有线性这段代码的好方法? 在Java中,我可能会使用return语句,但是这是Scala气馁。
我个人主张用火柴到处都可以,因为它在我看来,通常使代码可读性很强
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil =>
val ipv4it = ipConfigs.map { ipc =>
InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address]
}
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil =>
val taken = ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys
if (taken.nonEmpty) Unavailable(taken) else Valid
case dups => Duplicates(dups)
}
case invalid => Malformatted(invalid)
}
请注意,我选择相匹配,对else
的第一部分,因为你通常从具体到一般的比赛,因为Nil
是的一个子类Iterable
我把那个作为第一种情况下,省去了需要i if i.nonEmpty
在其他case
,因为这将是给定的,如果它不匹配Nil
另外一件事,这里要注意,所有的val
■不要需要明确定义类型,如果你喜欢写东西显著declutters代码
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
简单地
val ipAvailability = ipv4it.map(ip => (ip, isIpAvailable(ip)))
我也采取了消除许多一次性的变量,我没有找到远程必要的自由,因为他们所做的是增加更多的行代码
一个东西这里要注意有关使用match
了嵌套if
S,是,它更容易添加一个新的case
比它是添加新else if
中99%的时间,从而使其更加模块化,和模块化始终是一个好东西。
另外,通过纳撒尼尔福特所建议的,你可以把它分解成几个较小的方法,在这种情况下,上面的代码看起来就像这样:
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil => wellFormatted(ipConfigs)
case i => Malformatted(i)
}
def wellFormatted(ipConfigs: Iterable[IpConfig]): Result = {
val ipv4it = ipConfigs.map(ipc => InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address])
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil => noDuplicates(ipv4it)
case dups => Duplicates(dups)
}
}
def noDuplicates(ipv4it: Iterable[IpConfig]): Result =
ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys match {
case Nil => Valid
case taken => Unavailable(taken)
}
这有分裂成更小更易管理的块,同时保持具有的功能,只有做一件事情的FP理想的效益,但做的一两件事,而不是神的方法是做的一切。
你喜欢哪种风格,当然是你。
现在,这有一定的时间,但我会增加我的2美分。 处理这种情况的正确方法是要么。 您可以创建这样的方法:
def checkErrors[T](errorList: Iterable[T], onError: Result) : Either[Result, Unit] = if(errorList.isEmpty) Right() else Left(onError)
所以你可以使用的语法的理解
val invalidIpConfigs = getFormatErrors(ipConfigs)
val result = for {
_ <- checkErrors(invalidIpConfigs, Malformatted(invalidIpConfigs))
dups = getDuplicates(ipConfigs)
_ <- checkErrors(dups, Duplicates(dups))
taken = getAvailability(ipConfigs)
_ <- checkErrors(taken, Unavailable(taken))
} yield Valid
如果你不想返回要么使用
result.fold(l => l, r => r)
在检查方法的情况下使用期货(可能是getAvailability的情况下,例如),您可以用猫库能在一个干净的方式使用它: https://typelevel.org/cats/datatypes/eithert。 HTML
我认为这是相当可读的,我不会试图从那里改进,除了!isEmpty
等于nonEmpty
。
文章来源: Cleanest way in Scala to avoid nested ifs when transforming collections and checking for error conditions in each step