在Scala中,如何使用Option类重构代码?

时间:2015-04-09 04:52:06

标签: scala functional-programming refactoring type-conversion

在文件com.typesafe.play/play_2.11/srcs/play_2.11-2.3.8-sources.jar!/play/api/data/Form.scala中,我看到了这样一个函数的定义:

  protected def addPrefix(prefix: String) = {
    Option(prefix).filterNot(_.isEmpty).map(p => p + Option(key).filterNot(_.isEmpty).map("." + _).getOrElse(""))
  }

我认为这些代码可能需要几秒钟才能理解,并且可以进一步改进以更清晰。有没有人对什么是更好的重写方法有想法?

2 个答案:

答案 0 :(得分:1)

检查prefix是否为空字符串的方法,

Option(prefix).filterNot(_.isEmpty)

如果没有,请追加(非空字符串)key,否则返回空字符串。有很多方法可以在几行中重写,但不一定非常简洁,例如,

protected def addPrefix(prefix: String) = {
  (prefix,key) match {
    case ("",_) => ""
    case (p,"") => p
    case (p,k)  => p + "." + k
  }
}

请注意,原始代码处理空字符串

Option(null: String).filterNot(_.isEmpty)
res5: Option[String] = None

而这里建议的代码需要额外的检查,例如

def getStr(s: String) = Option(s).filterNot(_.isEmpty).getOrElse("")

因此将上述案例与(getStr(prefix), getStr(key))进行匹配,如下所示,

protected def addPrefix(prefix: String) = {
  (getStr(prefix), getStr(key)) match {
    case ("",_) => ""
    case (p,"") => p
    case (p,k)  => p + "." + k
  }
}

原始代码简洁易读,最重要的是不需要额外详细说明处理空值或覆盖所有可能的匹配情况,即语义是明确定义的。

可读性的一个可能的改进是定义一个函数,例如getStr(s: String)(这里提出),以缩短否则长的单行。

Justin建议使用的理解证明了一种强有力的替代方案。

答案 1 :(得分:1)

您可以尝试将其转换为for-comprehension

def addPrefix(prefix: String) =
  for{
    safePrefix <- Option(prefix)
    if !safePrefix.isEmpty
    safeKey <- (for{
      innerKey <- Option(key)
      if !innerKey.isEmpty
    } yield s".$innerKey").orElse(Option("")) 
  } yield s"$safePrefix$safeKey" 

但是,由于for的需要,内部orElse很混乱。所以,这是一个选择,有些人会考虑更好的理解......也许是个人偏好。实际上,它可以将内部for包装在一个方法中,这样可以提高可读性。

我对榆树使用匹配的方法很感兴趣,即使采用相同的方法,由于内在条件,它仍然看起来很乱:

def addPrefix(prefix: String) =  
  Option(prefix) match {
    case Some(prefix) if !prefix.isEmpty => Option(prefix + 
      (Option(key) match{
        case Some(key) if !key.isEmpty => s".$key"
        case _ => ""
      }))
    case _ => None
    }