关闭:为什么它们如此有用?

时间:2009-08-20 11:36:45

标签: oop functional-programming closures

作为OO开发人员,我可能很难看到它的价值。他们给了什么附加价值?它们适合OO世界吗?

10 个答案:

答案 0 :(得分:66)

您可以将其视为一个类的概括。

你的班级有一些州。它有一些其方法可以使用的成员变量。

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

您不必创建一个知道您希望函数使用的局部变量的类,而只需在现场定义函数,它就可以隐式访问当前可见的每个变量。

当您使用传统OOP语言定义成员方法时,其闭包是“此类中可见的所有成员”。

具有“正确”闭包支持的语言只是概括了这一点,因此函数的闭包是“这里可见的所有变量”。如果“ here ”是一个类,那么你有一个传统的类方法。

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

所以这只是一个概括,删除了“函数只能在类中定义”的愚蠢限制,但保持“函数可以看到任何变量在它们被声明的位置可见”的想法。

答案 1 :(得分:29)

闭包不会给你任何额外的力量。

你可以用它们实现的任何目标,你可以在没有它们的情况下实现。

但它们非常适用于使代码更清晰,更易读。 而且我们都知道干净可读的短代码是一种更容易调试并且包含更少错误的代码。

让我举几个可能用法的简短Java示例:

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

将被替换(如果Java有闭包):

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

答案 2 :(得分:28)

对我来说,闭包的最大好处是当你编写启动任务的代码,让任务运行,并指定任务完成时应该发生什么。通常,在任务结束时运行的代码需要访问开头可用的数据,并且闭包使这变得容易。

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

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

由于JavaScript的闭包,“requestID”变量被捕获在success函数内部。这显示了如何在同一位置编写请求和响应函数,并在它们之间共享变量。如果没有闭包,则需要将requestID作为参数传入,或者创建包含requestID和函数的对象。

答案 3 :(得分:5)

恕我直言,归结为能够捕获代码块,以便稍后在某些时候引用它们,并在/ if / as需要时执行。

它们似乎不是什么大不了的事情,并且闭包绝对不是你每天要完成任务所需的东西 - 但是它们可以使代码更简单,更清晰/管理。

[编辑 - 基于上述评论的代码示例]

爪哇:

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

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

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

答案 4 :(得分:4)

很可惜,人们不再在edu学习Smalltalk;在那里,闭包用于控制结构,回调,集合枚举,异常处理等。 对于一个很好的小例子,这里是一个工作队列动作处理程序线程(在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,这对于分叉的线程块也是如此。这就是使闭合如此容易使用的原因。

答案 5 :(得分:3)

以下是一些有趣的文章:

答案 6 :(得分:3)

闭包非常适合OO世界。

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

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

因此,闭包的使用倾向于在面向对象的代码中实现细节。

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

var typeName = configuration.GetType().AssemblyQualifiedName;

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

如果没有C#的闭包功能,那么将输入值(typeName)指定为Mock期望的一部分将非常困难。

答案 7 :(得分:0)

至于最后的查询:“Do(闭包)是否适合OO世界?”

在许多语言中,闭包以及一流的功能为设计OO程序提供了基础:Javascript,Lua和Perl浮现在脑海中。

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

  1. 函数第一类构造
  2. 在Java中,OO的实现细节(是否在内部使用闭包)不直接暴露给开发人员,因为它在Javascript,Lua和Perl中
  3. 因此,在Java这样的“传统OO”语言中,闭包的添加在很大程度上只是语法糖。

答案 8 :(得分:-2)

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

1)JavaScript是一种解释型语言,因此,对于评估大量输入的大型程序或程序,指令效率和命名空间保护对于更快,更灵敏的执行非常重要。

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

因此,闭包非常强大,并且在JavaScript中提供了卓越的效率。

答案 9 :(得分:-2)

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

  • 我很欣赏风格,状态隐藏部分和表达链接,但这还不够

  • 关于简单明确的代码

  • 可以说很多
  • 我觉得Scala,Perl和其他一些Scheme类型的语言旨在为语言设计师的特定思维方式提供一种速记,或者使它们“更有效率”

我会把python的简单性和java等早期语法作为简单和明确的更好方法

在狭小的空间内编写隐秘链式封盖可能会非常快。但是一切都可以通过简单的对象和算法来完成

需要关闭,但通常不能保证他们成为一等公民。