Scala风格:嵌套功能有多远?

时间:2012-07-08 20:16:50

标签: function scala coding-style nested

Scala的一个优点是它可以让您很好地控制范围。你可以窝 这样的功能:

def fn1 = {
  def fn11 = {
    ...
  }
  def fn12 = {
    ...
    def fn121 = {
      ...
    }
  }
  ...
  def fn13 = {
    ...
  }
}

这里的问题是fn1可能开始看起来有点令人生畏。来自Java背景,我们建议保持小到足以在IDE中的单个“页面”上查看的功能。

根据推理,你会考虑将fn12从fn1中取出来:“它现在只在fn1中使用,但它可能在以后的课堂其他地方有用...... “

此外,您是否会优先考虑嵌套函数的放置位置 - 在调用它们的代码之前或之后?

6 个答案:

答案 0 :(得分:6)

一般来说,我没有在实际代码中看到那么多函数嵌套。它违背了保持方法简单和简洁的精神。这样的嵌套主要用于你将使用外部作用域中的一些参数(例如递归函数的内部循环)的闭包,因此它比在外部声明它并且必须明确地重新传递这些参数更清晰。 / p>

您必须将嵌套函数放在调用它们的代码之前,或者它是转发引用,并且不会编译。 (在对象/类中,您可以将它们放在后面,但不放在方法中。)

答案 1 :(得分:5)

有一些模式可以利用一层嵌套。

递归,用于隐藏实现细节(并且比分成两个单独的顶级方法更清晰):

def callsRecursive(p: Param): Result = {
  def recursive(p: Param, o: OtherParam, pr: PartialResult = default): Result = {
    ...
  }
}

范围安全不要重复自己:

def doesGraphics(p: Point) {
  def up(p: Point): Point = // ...
  def dn(p: Point): Point = // ...
  def lf(p: Point): Point = // ...
  def rt(p: Point): Point = // ...
  /* Lots of turtle-style drawing */
}

更多深奥的技巧,例如为本地块隐藏隐式转换。

如果你需要这两个,我可以设想嵌套两次。更多的可能是矫枉过正,主要是因为你可能会让一种方法做得太多。你应该考虑如何用干净的接口细分问题,然后可以成为他们自己的方法,而不是在方法中定义的各种变量周围有一个混乱的大杂烩。大方法就像全局变量:一切都变得过于依赖于实现的细节而且很难跟踪。如果你已经准备好做适当的思考以使某些东西具有合适的界面,即使你只需要它一次,那么考虑把它带到顶层。如果你不想那么难,我倾向于把它留在里面以避免污染界面。

无论如何,不​​要害怕在任何需要的地方创建方法。例如,假设您发现自己深入某个方法中有两个集合,每个集合必须在逻辑中的特定点对它们执行相同的操作。如果您有一两种或三种方法,请不要担心!只需在需要的地方创建方法,然后调用它而不是重复自己。 (请记住,如果您只需要在同一个地方处理多件事情,那么创建列表和映射是另一种选择。)

答案 2 :(得分:2)

我认为始终使用最低可见性是最佳做法。如果不同函数需要嵌套函数,则无论如何都可以移动它。

答案 3 :(得分:2)

如果你有一个顶级功能,就像你所描述的那样,它可能做得很多。

如果出现这种情况,TDD也会帮助做出决定:仍然可以轻松测试所有内容。

如果我得出的结论是实际上是这种情况,我会重构内部函数作为依赖项,并使用自己的测试。

在最终结果中,我对定义的函数中定义的函数的使用非常有限......我还对方法大小设置了更严格的限制:在java中大约10-15行,在scala中更少,因为它不那么详细。

我把内部函数主要放在外部方法的顶部,但它很重要,因为它无论如何都很短。

答案 4 :(得分:0)

看起来确实非常可怕!如果要精细控制私有方法的范围,可以将它们声明为

private[scope] def fn12 = { ... } 

其中scope是一个包。您可以在The busy Java developer's guide to Scala中阅读更多内容。

我个人避免嵌套命名方法(def),而我不介意嵌套匿名函数(例如,连续传递样式编程中的闭包)。

答案 5 :(得分:0)

嵌套函数很有用(例如递归中的帮助器)。但如果它们变得太多,那么没有什么可以阻止你将它们提取到一个新类型并委托给它。