为什么添加:返回Smalltalk集合中添加的对象?

时间:2012-12-27 00:24:36

标签: oop smalltalk pharo squeak visualworks

背景

捕获每一个Smalltalk新手的东西是add:不会返回“self”但是要添加的对象。

例如,使用以下代码:

myCollection := OrderedCollection new 
  add: 'Peter';
  add: 'John';
  add: 'Paul'.

myCollection将包含字符串“Paul”,而不包含集合本身。

这是因为add:返回正在添加的对象,整个级联表达式的计算结果为最后发送的消息。

相反,最后应该用yourself编写:

myCollection := OrderedCollection new 
  add: 'Peter';
  add: 'John';
  add: 'Paul';
  yourself.

问题

  • 为什么会这样?
  • 这样设计的是什么?
  • add:以这种方式行事有什么好处?

5 个答案:

答案 0 :(得分:12)

我已经考虑过这个了。我从来没有听说过Smalltalk的任何原始设计师为这个决定辩护,所以我们不确定他们为什么这么做。我之所以认为原因是因为级联。如果添加:返回接收器,那么(东西添加:thing1)add:thing2将与添加的东西相同:thing1;添加:thing2。通过添加:返回参数,这两个表达式是不同的,程序员可以在适当的时候使用它们。

但是,我认为这是一个错误。我一直在教Smalltalk超过25年,每次教我都会遇到麻烦。我总是警告他们,但他们仍然会犯错误:所以,我认为这是一个糟糕的设计决定。

这个设计决定是关于库,而不是编译器。您可以通过进入集合类并更改它们来更改它。当然,无法预测有多少Smalltalk程序会破坏。收藏是如此根本,以至于这种改变很难成为对语言的真正改变。

答案 1 :(得分:6)

在其他语言中,您可以写:

b[j] = a[i] = e;

如果at:put:返回put对象,则以某种方式保留在Smalltalk中:

collectionB at: j put: (collectionA at: i put: e).

允许这种链接的add: / remove:存在同样的兴趣:

collectionB add: (collectionA add: anElement).
collectionB add: (collectionA remove: anElement).

答案 2 :(得分:3)

最好级联这样的方法发送,永远不要依赖它们的返回值。它与setter方法相同,有时它们可​​能会返回self,有时它们会返回参数。最安全的做法是假设这些方法的返回值接近随机,永远不会使用它。

答案 3 :(得分:2)

我无法辩护,也无法反驳拉尔夫的经历。

对对称的渴望可能是一个促成因素。鉴于#remove:返回被删除的对象,让#add:返回添加的对象是有道理的。

我认为,简单的例子也会让我们产生偏见。当我们已经将对象添加到变量中,或者它是一个简单的文字时,返回的值似乎毫无意义。但是如果我们有(可疑的)代码看起来像这样:

someProfile add: VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd

如果someProfile是一个线性列表,我想你可以获取你刚添加的值:'ed via last。但它可能只是一个包或一套。在这种情况下,它可以很方便:

currentSize := someProfile add: VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd

有些人会认为这比:

更好
someProfile add: (currentSize := VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd)

虽然最好的是:

currentSize := VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd.
someProfile add: currentSize

答案 4 :(得分:0)

我提出的最佳解释是使其等同于作业。假设你有这样的代码:

(stream := WriteStream on: String new) nextPutAll: 'hello'.
stream nextPut: $!

分配给变量会计算分配的对象。我应该能够用集合替换变量并获得相同的行为:

(array at: 1 put: (WriteStream on: String new)) nextPutAll: 'hello'.
(array at: 1) nextPut: $!

现在,说了这句话,我会惩罚任何编写此代码的开发人员,因为它不可读。我宁愿把它分成两行:

array at: 1 put: (WriteStream on: String new).
array first
   nextPutAll: 'hello';
   nextPut: $!

这是我能给出的最好的理由。这样做是为了将赋值转换为与变量赋值一致的集合,但是如果你利用了这个特性,你就会有难以阅读的代码。