Scala中干净的方法,以避免转化集合和在每个步骤中检查错误条件时嵌套IFS(Cleanest wa

2019-10-30 11:14发布

我有验证,看起来像下面的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气馁。

Answer 1:

我个人主张用火柴到处都可以,因为它在我看来,通常使代码可读性很强

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理想的效益,但做的一两件事,而不是神的方法是做的一切。

你喜欢哪种风格,当然是你。



Answer 2:

现在,这有一定的时间,但我会增加我的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



Answer 3:

我认为这是相当可读的,我不会试图从那里改进,除了!isEmpty等于nonEmpty



文章来源: Cleanest way in Scala to avoid nested ifs when transforming collections and checking for error conditions in each step
标签: scala