【问题标题】:Is the arrow anti pattern a standard in Scala箭头反模式是Scala中的标准吗
【发布时间】:2013-06-05 14:54:47
【问题描述】:

第一个问题:箭头反模式是Scala中的做事方式吗?

我已经从 Java 过渡到 Scala 大约 3 个月了。我开始将反箭头模式视为 Scala 中的一种标准。

例如,在 Java 中,我喜欢尽快从方法返回。我们不喜欢很多内部嵌套。如果你有很多,它被称为箭头反模式。你可以在这里阅读:http://c2.com/cgi/wiki?ArrowAntiPattern

Java 示例:

boolean isValid(String input){
  if (input.trim().length() <1) return false //return early
  if (input.substring(0,3).equals("sth")) return false;

  //do some more checks for validity then return true

  return true
}

如果我在 Scala 中编写此代码,我将不得不使用匹配大小写语句,这会导致大量缩进。请记住,这只是一个虚拟示例,我尝试仅传达 Scala 的嵌套和缩进。

def isValid(input:String):  Boolean = {
  (input.trim.length < 1) match {
    case true => 
      input.substring(0,3) match {
        case sthCase if(sthCase=="sth") =>
          //do some more checks etc

          //eventually arrive at the "true" branch
               ... case valid => true

        case sthElse if (sthCase=="bla") => //some more code etc
        case _ => false
      }
    case _ => false
  }
}

我知道这个例子的编写方式可能与我编写 Java 的方式相同,但是如果我不想返回 true 或 false,而是想将该值分配给一个变量,即:val valid = (input.trim.length &lt; 1) match ...,那么这是唯一的方法因为你不能重新分配给 val。我可以使用var 而不是val,但是我会使用Java 而不是Scala。

问题,再次重申: 所以问题是,箭头反模式是在 Scala 中做事的方式还是我错过了什么?在 Scala 中是否有更好的方法来完成我刚刚编写的内容(请记住,我希望将匹配语句结果分配给 val 变量)。

【问题讨论】:

    标签: scala coding-style nested standards anti-patterns


    【解决方案1】:

    请注意,只要代码的所有部分都是单入口/单出口,您始终可以在 Scala 中使用分解。这是因为 Scala 中的嵌套函数可以访问外部函数的局部变量。例如:

    def isValid(input: String): Boolean = {
      def testLength = input.trim.length >= 1
      def testSomething = input.substring(0, 3) == "sth"
    
      return testLength && testSomething
    }
    

    因此,您可以根据需要将任何类似箭头的结构分解为其子组件。

    还要注意,使用 match 来测试布尔值或比较某些东西是否相等是多余的。

    【讨论】:

    • 这也不是一种真正的新技术。见"Program Development by Stepwise Refinement" by Niklaus Wirth。这可能是 Pascal 语言家族中的所有语言都有嵌套过程/函数的主要原因。
    • 确实如此,但这是一个开始。我可能会再发一篇文章继续这个问题,但比嵌套的一些布尔值更深入/更高级。再次感谢
    • return 关键字可以而且应该省略
    • 作为一种风格偏好,我喜欢在长函数的末尾明确地使用return。它不仅可以更容易地直观地找到函数的结尾(假设您有语法突出显示),它还清楚地表明我们正在处理一个函数而不是一个过程,而无需检查函数签名。两种方式的语义都是相同的,所以客观上没有一种方式更好。
    • 如果你省略了return,它会让你的源代码“grep-able”returns 可以被其他东西替换。
    【解决方案2】:

    您的 java 示例可以在 scala 中轻松转换为 if/else,无需显式返回,也无需任何额外的嵌套级别。

    def isValid(input: String) = {
      if (input.trim().length() <1) false
      else if (input.substring(0,3).equals("sth")) false
      else true
    }
    

    if/else 就像 scala 中的一切都是一个表达式并且有一个返回值,所以你不妨将它用于赋值:

    val isValid =
      if (input.trim().length() <1) false
      else if (input.substring(0,3).equals("sth")) false
      else true
    

    或者在这种情况下你也可以简单地写

    val isValid = (input.trim().length() >= 1) && (!input.substring(0,3).equals("sth"))
    

    【讨论】:

    • 我明白你在说什么。我应该使用一个非布尔示例来说明我的观点。
    【解决方案3】:

    如果一个函数中有太多returns,则缺少函数:

    def isValid(s: String): Boolean = {
      def isLengthValid = s.trim().length() > 0
      def shouldNotContainsSomething = s.substring(0, 3) != "sth"
    
      isLengthValid && shouldNotContainsSomething
    }
    

    【讨论】:

    • 在 Scala 中无需使用equals 进行字符串比较。 == 运算符自动使用equals 方法。
    • 它是从 Java 示例中复制的 - 我只是附加了 == false 以保留条件。所以应该是!=。我已经更新了。
    【解决方案4】:

    不要做货物崇拜just because some rule says so。 Scala 的好处是你可以两全其美——例如有时,以可变风格编写的算法比不可变风格更清晰。

    顺便说一下,还有require方法,常用于验证失败(但会出现异常)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多