"关闭是穷人的对象,反之亦然" - 这是什么意思?

mis*_*tor 57 oop closures functional-programming object

闭包是穷人的对象,反之亦然.

我已经看到这个声明 许多 地方网站(上包括SO),但我不太明白是什么意思.有人可以解释它究竟意味着什么吗?

如果可能,请在答案中包含示例.

mis*_*tor 61

对象是穷人的封闭.

考虑Java.Java是一种面向对象的编程语言,对真正的词法闭包没有语言级支持.作为一个解决方案的Java程序员使用匿名内部类,它可以关闭词法范围中可用的变量(前提是它们final).从这个意义上讲,对象是穷人的封闭.

关闭是穷人的对象.

考虑一下Haskell.Haskell是一种函数式语言,对真实对象没有语言级支持.然而,他们可以使用闭包中所述进行建模,由奥列格Kiselyov和拉尔夫Lammel优秀论文.从这个意义上说,封闭是穷人的对象.


如果你来自OO背景,你可能会发现对象更自然的思考,因此可能会认为它们是一个比封闭更基本的概念.如果你来自FP背景,你可能会发现闭包更加自然,因此可能会认为它们是一个比对象更基本的概念.

故事的道德是封闭和对象是彼此可表达的想法,没有一个比另一个更基本.这就是正在审议的声明.

在哲学中,这被称为模型依赖的现实主义.

  • 这有一个小的区别,在对象中,状态是在对象实例化上明确定义的.在封闭中,状态来自未知的外部范围.因此,使用对象会产生更多解耦,显式和可测试的代码. (4认同)
  • @aoeu256 可变性是一个正交维度。您可以关闭普通闭包和对象中的可变变量。类似地,您可以选择在对象中以及普通闭包中仅使用不可变变量。 (4认同)
  • @dtheodor我不同意您的说法:封闭状态始终可以通过从上至下的阅读来预测。但是,对象的状态(至少是面向对象的样式)恰好是当前状态,因为状态和代码是混合在一起的。闭包是[引用透明的](https://en.wikipedia.org/wiki/Referential_transparency),而对象不是。 (3认同)
  • 某些语言需要在闭包中捕获变量,这意味着范围是明确定义的. (2认同)
  • 我经常使用 Python,并且发现在大多数情况下闭包更具可读性,因为对象需要大量代码来进行构造函数、方法和数据初始化。对象往往会改变其内部状态,从而导致不习惯它们的程序员出现类似“goto”的错误,而闭包往往用于状态变化不大的对象。对我来说,闭包看起来像“for 循环”,而对象看起来像“while 循环”,因为 for 循环需要更少的代码,更常见,并且错误范围更小。然而,有时需要 while 循环。 (2认同)

Ben*_*ter 57

关键是闭包和对象实现了相同的目标:在单个逻辑单元中封装数据和/或功能.

例如,您可以创建一个代表狗的Python类:

class Dog(object):
    def __init__(self):
        self.breed = "Beagle"
        self.height = 12
        self.weight = 15
        self.age = 1
    def feed(self, amount):
        self.weight += amount / 5.0
    def grow(self):
        self.weight += 2
        self.height += .25
    def bark(self):
        print "Bark!"
Run Code Online (Sandbox Code Playgroud)

然后我将类实例化为一个对象

>>> Shaggy = Dog()
Run Code Online (Sandbox Code Playgroud)

Shaggy对象内置了数据和功能.当我打电话时Shaggy.feed(5),他获得了一英镑.该磅存储在作为对象属性存储的变量中,这或多或少意味着它在对象内部范围内.

如果我正在编写一些Javascript,我会做类似的事情:

var Shaggy = function() {
    var breed = "Beagle";
    var height = 12;
    var weight = 15;
    var age = 1;
    return {
        feed : function(){
            weight += amount / 5.0;
        },
        grow : function(){
            weight += 2;
            height += .25;
        },
        bark : function(){
            window.alert("Bark!");
        },
        stats : function(){
            window.alert(breed "," height "," weight "," age);
        }
    }
}();
Run Code Online (Sandbox Code Playgroud)

这里,我在一个函数中创建了一个范围,然后调用了该函数,而不是在一个对象中创建一个范围.该函数返回由某些函数组成的JavaScript对象.因为这些函数访问在本地作用域中分配的数据,所以不回收内存,允许您通过闭包提供的接口继续使用它们.

  • 澄清一下:JavaScript也支持OOP - 尽管没有'class'关键字. (5认同)
  • 虽然不一样的哎呀。向kyle Simpson询问。 (2认同)

P D*_*ddy 14

最简单的对象只是在该状态下运行的状态和函数的集合.闭包也是状态的集合和在该状态下运行的函数.

假设我调用一个需要回调的函数.在这个回调中,我需要在函数调用之前对某些已知的状态进行操作.我可以创建一个体现此状态的对象("fields")并包含一个作为回调执行的成员函数("method").或者,我可以采取快速简便的("穷人")路线并创建一个封闭.

作为对象:

class CallbackState{
    object state;

    public CallbackState(object state){this.state = state;}

    public void Callback(){
        // do something with state
    }
}

void Foo(){
    object state = GenerateState();
    CallbackState callback = new CallbackState(state);
    PerformOperation(callback.Callback);
}
Run Code Online (Sandbox Code Playgroud)

这是伪C#,但在概念上与其他OO语言类似.正如您所看到的,回调类涉及到管理状态的大量样板文件.使用闭包会更简单:

void Foo(){
    object state = GenerateState();
    PerformOperation(()=>{/*do something with state*/});
}
Run Code Online (Sandbox Code Playgroud)

这是一个lambda(同样,在C#语法中,但概念在支持闭包的其他语言中类似),它为我们提供了类的所有功能,而无需编写,使用和维护单独的类.

你还会听到一个必然结果:"对象是一个穷人的封闭".如果我不能或不会利用闭包,那么我被迫使用对象来完成他们的工作,就像我的第一个例子.虽然对象提供了更多的功能,但是由于已经说明的原因,闭包通常是封闭工作的更好选择.

因此,没有对象的穷人通常可以通过闭包完成工作,而没有闭包的穷人可以使用对象完成工作.富人同时拥有并为每项工作使用正确的人.


cod*_*eim 7

编辑:问题的标题不包括"反之亦然"所以我会尽量不去假设提问者的意图.

两个共同阵营是功能性和命令式语言.两者都是可以通过不同方式以不同方式完成类似任务的工具.

关闭是穷人的对象.

对象是穷人的封闭.

单独地,每个陈述通常意味着作者有某种偏见,通常以一种语言或一种语言与另一种语言或语言的不适为基础.如果不是偏见,它们可能受到一个环境或另一个环境的约束.我读到的作者说,这种事情通常是狂热者,纯粹主义者或语言宗教类型.如果可能的话,我会避免语言宗教类型.

关闭是穷人的对象.对象是穷人的封闭.

这位作者是一位"实用主义者",也非常聪明.这意味着作者欣赏这两种观点并认识到它们在概念上是相同的.这是我的同伴.


小智 6

只是这么多糖,因为封闭物隐藏了裙子下面的匿名物品.

  • +1 - 在VM /编译器级别,一切都是某种"对象".这一切都在一个名字中.我们这些实施编译器的人知道问题是相似的.必须进行垃圾收集的任何东西都是内心深处的一个对象. (2认同)
  • 实际上,这是一个虚拟答案.它们与编译器或解释器是否相同并不重要.重要的是它们作为抽象的价值.否则,我可以说一切都只有0和1,这不是很有用. (2认同)

Chu*_*uck 5

"对象是一个穷人的封闭"不仅仅是一些理论等价的陈述 - 它是一种常见的Java习语.使用匿名类来包装捕获当前状态的函数是很常见的.以下是它的使用方法:

public void foo() {
    final String message = "Hey ma, I'm closed over!";
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            System.out.println(message);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

这甚至看起来很像使用另一种语言的闭包的等效代码.例如,使用Objective-C块(因为Objective-C与Java相似):

void foo() {
    NSString *message = @"Hey ma, I'm closed over!";
    [[NSOperationQueue currentQueue] addOperationWithBlock:^{
        printf("%s\n", [message UTF8String]);
    }];
}
Run Code Online (Sandbox Code Playgroud)

唯一真正的区别是功能包装在new Runnable()Java版本的匿名类实例中.