closures:为什么他们如此有用?

作为面向对象的开发者,也许我很难看到它的价值。 他们给了什么附加值? 他们适合在面向对象的世界吗?

closures不会给你任何额外的权力。

任何你可以用他们实现的事情都可以在没有他们的情况

但是它们对于使代码更清晰和可读性非常有用。 正如我们都知道干净可读短代码是一个更容易debugging,并包含更less的错误的代码。

让我给你简短的可能用法Java示例:

button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Pressed"); } }); 

将被replace(如果Javaclosures)与:

 button.addActionListener( { System.out.println("Pressed"); } ); 

你可以把它看作是一个类的泛化。

你的课程有一些状态。 它有一些成员variables,它的方法可以使用。

闭包只是给函数访问本地状态更方便的方法。

您不必创build一个知道您希望函数使用的局部variables的类,就可以简单地定义该函数,并且可以隐式地访问当前可见的每个variables。

当你用传统的OOP语言定义一个成员方法时,它的closures是“在这个类中可见的所有成员”。

具有“适当的”closures支持的语言只是概括了这一点,所以函数的closures是“所有的variables在这里可见”。 如果“ 这里 ”是一个类,那么你有一个传统的类方法。

如果“ here ”在另一个函数内部,那么函数程序员就会认为它是一个闭包。 您的函数现在可以访问在父函数中可见的任何内容。

所以这只是一个泛化,消除了“函数只能在类中定义”这个愚蠢的限制,而是保留了“函数能够在声明的地方看到任何variables是可见的”的想法。

对我来说,closures的最大好处是当你编写启动任务的代码,离开任务运行,并指定任务完成后应该发生什么。 通常,在任务结束时运行的代码需要访问开始时可用的数据,而closures可以使这一点变得简单。

例如,JavaScript中常见的用途是启动HTTP请求。 无论谁启动它可能想控制响应到达时发生的事情。 所以你会做这样的事情:

 function sendRequest() { var requestID = "123"; Ext.Ajax.request({ url:'/myUrl' success: function(response) { alert("Request " + requestID + " returned"); } }); } 

由于JavaScript的closures,“requestID”variables被捕获到成功函数中。 这显示了如何在同一个地方写请求和响应函数,并在它们之间共享variables。 没有闭包,你需要传递requestID作为参数,或者创build一个包含requestID和函数的对象。

恕我直言,它归结为能够捕获的代码块他们的上下文被引用在稍后的某个时刻,并执行/如果/如果需要。

它们似乎不是什么大不了的事情,而且closures并不是每天都需要完成的事情 – 但是它们可以使代码编写更简单,更清洁的编写/pipe理。

[编辑 – 基于上面评论的代码示例]

Java的:

 List<Integer> numbers = ...; List<Integer> positives = new LinkedList<Integer>(); for (Integer number : integers) { if (number >= 0) { positives.add(number); } } 

Scala(跳过一些其他的细节,比如types推断和通配符,所以我们只比较闭包的效果):

 val numbers:List[Int] = ... val positives:List[Int] = numbers.filter(i:Int => i >= 0) 

很遗憾,人们不再在edu上学习Smalltalk了; 在那里,闭包被用于控制结构,callback,枚举枚举,exception处理等等。 对于一个很好的例子,下面是一个工作者队列动作处理程序线程(在Smalltalk中):

 |actionQueue| actionQueue := SharedQueue new. [ [ |a| a := actionQueue next. a value. ] loop ] fork. actionQueue add: [ Stdout show: 1000 factorial ]. 

对于那些不能阅读Smalltalk的人来说,JavaScript语法也是一样的:

 var actionQueue; actionQueue = new SharedQueue; function () { for (;;) { var a; a = actionQueue.next(); a(); }; }.fork(); actionQueue.add( function () { Stdout.show( 1000.factorial()); }); 

(就像你看到的那样:语法有助于阅读代码)

编辑:注意如何在块内引用actionQueue,即使对于分叉的线程块也是如此。 那是什么使闭包很容易使用。

这里有一些有趣的文章:

  • 穿越边界:闭幕 – 布鲁斯·泰特(Bruce Tate)提到封锁的一些优点
  • closures的定义 – Neal Gafter(发明Javaclosuresbuild议之一的人之一)

OO世界中的闭合非常适合。

举一个例子,考虑一下C#3.0:它有闭包和许多其他的function方面,但仍然是一个非常面向对象的语言。

根据我的经验,C#的function方面倾向于保持在类成员的实现中,而不是我的对象最终暴露的公共API的一部分。

因此,闭包的使用往往是其他面向对象代码中的实现细节。

我一直使用它们,因为我们的一个unit testing(针对Moq )的代码片段显示:

 var typeName = configuration.GetType().AssemblyQualifiedName; var activationServiceMock = new Mock<ActivationService>(); activationServiceMock.Setup(s => s.CreateInstance<SerializableConfigurationSection>(typeName)).Returns(configuration).Verifiable(); 

如果input值(typeName)不是用于C#的闭包特性,那么将input值(typeName)指定为Mock期望值的一部分就相当困难了。

至于最后的问题:“Do(封闭)适合面向对象的世界吗?”

在许多语言中,闭包和一stream的函数为devise面向对象程序提供了基础:JavaScript,Lua和Perl。

在我熟悉的另一个更传统的OO语言Java中,有两个主要的区别:

  1. 函数不是一stream的结构
  2. 在Java中,OO的实现细节(不pipe是在内部使用闭包)不直接暴露给开发人员,因为它是Javascript,Lua和Perl

因此,在诸如Java这样的“传统面向对象”语言中,闭包的增加主要是句法糖。

也许在编程编程的世界里,闭包的好处并不明显。 在JavaScript中,闭包非常强大。 这是有两个原因的:

1)JavaScript是一种解释型语言,因此对于评估大量input的大型程序或程序来说,指令的效率和名称空间的保存对于更快,更有响应性地执行来说是非常重要的。

2)JavaScript是一种lambda语言。 这意味着在JavaScript函数中,第一类对象定义了父级函数的作用域和作用域,子对象可以访问它们。 这是很重要的,因为variables可以在父函数中声明,在子函数中使用,并且即使在子函数返回后也保留值。 这意味着一个variables可以被一个函数重复使用多次,该函数的最后一次迭代已经定义了一个值。

因此,闭包非常强大,在JavaScript中提供更高的效率。

看上面的例子,我可以加我一点。

  • 我欣赏风格,隐藏的部分和expression的链接,但这是不够的

  • 简单和明确的代码可以说很多

  • 我觉得Scala,Perl和其他一些Schemetypes的语言是为了让语言devise者对特定的思维方式进行简化,或者让他们“更有效率”

我会把Python的简单性和Java的早期语法作为简单和明确的更好的方法

在狭窄的空间里写下隐秘的链式封闭可能是相当快的。 然而,一切都可以通过简单的对象和algorithm来完成

closures是必要的,但并不经常让他们成为头等公民。