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中,有两个主要的区别:
- 函数不是一stream的结构
- 在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是必要的,但并不经常让他们成为头等公民。