“封闭是穷人的对象,反之亦然”-这是什么意思?

封闭是穷人的对象,反之亦然。

我已经在网上看到这个声明 很多 地方(包括所以) ,但我不太明白它的意思。有人能解释一下这到底是什么意思吗?

如果可能的话,请在你的回答中包括例子。

17348 次浏览

一个对象,在其最简单的,只是一个集合的状态和函数操作的状态。闭包也是一个状态集合和一个对该状态进行操作的函数。

假设我调用一个接受回调的函数。在这个回调中,我需要对函数调用之前已知的某个状态进行操作。我可以创建一个体现这种状态的对象(“字段”) ,并包含一个作为回调执行的成员函数(“方法”)。或者,我可以采取快速和简单的(“穷人的”)路线,创造一个结束。

作为一个对象:

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);
}

这是伪 C # ,但在概念上与其他面向对象语言相似。正如您所看到的,回调类管理状态涉及大量的样板。使用闭包会简单得多:

void Foo(){
object state = GenerateState();
PerformOperation(()=>{/*do something with state*/});
}

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

你还会听到推论: “物体是穷人的封闭”。如果我不能或不想利用闭包,那么我就不得不使用对象来完成它们的工作,就像我的第一个示例一样。虽然对象提供了更多的功能,但由于上面已经说过的原因,闭包通常是闭包工作的更好选择。

因此,没有对象的穷人通常可以用闭包完成工作,没有闭包的穷人可以用对象完成工作。一个有钱人两者都有,而且每份工作都用对了人。

编辑: 问题的标题不包括“反之亦然”,所以我尽量不假设问题者的意图。

两个共同的阵营是函数式语言和命令式语言。这两种工具都可以使用不同的关注点集以不同的方式完成相似的任务。

封闭是穷人的目标。

物体是穷人的封闭物。

就个人而言,每个陈述通常意味着作者有一些偏见,这样或那样,通常植根于他们对一种语言或一类语言的舒适与对另一种语言的不适。如果没有偏见,他们可能会受到一个或另一个环境的约束。我读过的那些说这类事情的作者通常是狂热分子、纯粹主义者或语言宗教人士。如果可能的话,我尽量避免使用宗教语言。

封闭是穷人的对象,对象是穷人的封闭。

这本书的作者是一位“实用主义者”,而且相当聪明。这意味着作者欣赏这两种观点,并且欣赏它们在概念上是一致的。这是我喜欢的类型。

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

例如,您可以创建一个 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!"

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

>>> Shaggy = Dog()

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);
}
}
}();

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

“对象是穷人的闭包”不仅仅是对某种理论等价性的陈述ーー这是 Java 的一个常见习语。使用匿名类来包装捕获当前状态的函数是非常常见的。以下是使用方法:

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

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

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

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

就是这么多的糖,因为关闭隐藏匿名对象在他们的裙子下面。

物体是穷人的封闭物。

以 Java 为例。Java 是一种面向对象程序设计语言,不支持真正的词法闭包。作为一种解决方案,Java 程序员使用匿名内部类,可以关闭词法范围内可用的变量(如果它们是 final的话)。在这个意义上,对象是穷人的闭合。

封闭是穷人的目标。

想想 Haskell。Haskell 是一种函数式语言,不支持实际对象的语言级别。然而,它们可以使用闭包进行建模,正如 Oleg Kiselyov 和 Ralf Lammel 在 这个优秀论文中所描述的那样。从这个意义上说,闭包是穷人的对象。


如果您来自 OO 背景,您可能会发现从对象的角度思考更自然,因此可能认为它们是比闭包更基本的概念。如果您来自 FP 背景,您可能会发现用闭包来思考更自然,因此可能会认为闭包是一个比对象更基本的概念。

这个故事的寓意就是 闭包和对象是可以相互表达的概念,没有一个比另一个更基本。这就是我们正在考虑的声明。

在哲学上,这被称为 依赖模型的实在论

对象可以作为闭包的替代品,这一点很容易理解,只需将捕获的状态放在对象中,并将调用作为方法。实际上,例如 C + + lambda 闭包是作为对象实现的(对于 C + + 来说,事情有点棘手,因为语言不提供垃圾收集,真正的带有可变共享状态的闭包因为捕获的上下文的生命周期而很难正确实现)。

相反(闭包可以用作对象)很少被观察到,但是它是一种非常强大的技术... 例如(Python)

def P2d(x, y):
def f(cmd, *args):
nonlocal x, y
if cmd == "x": return x
if cmd == "y": return y
if cmd == "set-x": x = args[0]
if cmd == "set-y": y = args[0]
return f

函数 P2d返回一个捕获 xy两个值的闭包。然后,闭包提供了使用命令对它们进行读写的访问。比如说

p = P2d(10, 20)
p("x") # --> 10
p("set-x", 99)
p("x") # --> 99

因此闭包的行为类似于一个对象; 而且,由于任何访问都是通过命令接口进行的,因此很容易实现委托、继承、计算属性等。

“放过 Lambda”这本很好的书是以 Lisp 作为一种语言来构建这个想法的,但是任何支持闭包的语言都可以使用这种技术(在 Lisp 中的优点是你也可以使用宏来改变语法,读取宏来提高可用性并自动生成所有样板代码)。这本书的标题正是关于这个... 一个 let包装一个 lambda:

(defun p2d (x y)
(let ((x x) (y y))
(lambda (cmd &rest args)
(cond
((eq cmd 'x) x)
((eq cmd 'y) y)
((eq cmd 'set-x) (setq x (first args)))
((eq cmd 'set-y) (setq y (first args)))))))

实际上,我不确定我是否同意这种方法中的“可怜的”形容词。