在GNU Smalltalk中使用和不使用“ new”创建的实例之间的区别

时间:2019-03-24 16:22:40

标签: smalltalk gnu-smalltalk

两者之间有什么区别

Rectangle origin: 5@5 extent: 40@30

Rectangle new origin: 5@5 extent: 40@30

3 个答案:

答案 0 :(得分:4)

Rectangle new origin: 5@5 extent: 40@30创建一个Rectangle的完全初始化的实例(精确地说,所有坐标都设置为0),然后使用origin:extend:accessor方法设置其坐标和范围。

Rectangle origin: 5@5 extent: 40@30使Rectangle类构造了具有给定属性的Rectangle实例,但是看起来合适。对于GNU Smalltalk,它使用basicNew消息分配Rectangle实例而不是新的(see the source of Rectangle)。这就避免了上述变体的“完全初始化的实例”状态:它跳过任何初始化并仅分配内存(GNU Smalltalk文档没有明确说明,但这传统上是basicNew的目的)。然后,它使用origin:extend:访问器来初始化新实例的坐标和范围。

答案 1 :(得分:3)

这是风格问题。 Rectangle类提供了一种用于创建实例的便利方法,因此您可以直接与该类进行通信并减少编写代码。这也是一个好习惯,因为您创建具有正常工作所需的所有矩形对象(此惯例称为RAII,资源获取是初始化)。如果您查看Rectangle class>>#origin:extent:的来源,就会发现类似

origin: aPoint extent: anotherPoint
    ^self new origin: aPoint extent: anotherPoint

因此,实际上直接将消息发送给类o手动创建它,然后进行设置在实践中是相同的

答案 2 :(得分:1)

我认为重要的一点是,要注意Smalltalk与其他OO语言之间的区别。

在其他OO语言中,您有一个称为构造函数的构造。这样,您可以在调用方法new时自动运行某些代码。

例如,在红宝石中,您会这样做

# Class name   
class ConstructorExample  
    # A constructor  
    def initialize    
        puts "I'm now running constructor"
    end   
end    

# Creating Object 
ConstructorExample.new # upon which calling you will get initialized run automatically.

输出将在您的shell中:

> I'm now running constructor

Smalltalk 中,您必须区分newbasicNew。 (有时new只是basicNew的别名,因此您必须手动运行initialize 或创建类方法。 basicNew不会自动执行初始化,new通常会执行(并非所有方言!)。

上面的示例可以写成:

Object subclass:#ConstructorExample
        instanceVariableNames:''
        classVariableNames:''
        poolDictionaries:''
        category: ''

ConstructorExample>>initialize
    Transcript showCR: 'I'm now running a constructor'


"You would then instantiate"
ConstructorExample new

| example |
example := ConstructorExample basicNew.
example initialize "sending method initialize like any other method"

在两种情况下,输出都是(在您的成绩单中):

I'm now running a constructor

在我看来,这样做的主要原因是,如果有类方法,则可以在一些自定义代码之后运行构造函数

ConstructorExample class >> run
    ^ self basicNew; Transcript showCR: 'Running before constructor'; self initialize; yourself

那么您只需做:

ConstructorExample run

输出为:

Running before constructor
I'm now running a constructor

现在来看您的例子

正如 JayK melkyades 解释的主要区别,我将在其上提供更多的不同之处(细节):

第一:

Rectangle new origin: 5@5 extent: 40@30

这实际上是做什么的(没有Transcript showCR:):

| myRactangle |
myRactangle := Ractangle new. "Creates an empty instance with origin: 0 @ 0 corner: 0  @ 0 and would execute initialize if there would be one." 
Transcript showCR: 'Current origin: ', origin asString, 'with corner: ', corner asString. "You should see the zeros"
myRactangle origin: 5@5 extent: 40@30
Transcript showCR: 'Current origin: ', origin asString, 'with corner: ', corner asString. "You should your custom point(s)"

Ractangle new发生了什么?

Rectangle class >> new [
    "Answer the (0 @ 0 corner: 0 @ 0) rectangle"

     <category: 'instance creation'>
     ^self origin: 0 @ 0 corner: 0 @ 0
]

当您检查源代码时,sets origin: 0 @ 0 corner: 0 @ 0(请注意通过... corner:而不是extent:进行设置)

第二:

Rectangle origin: 5@5 extent: 40@30

源代码:

Rectangle class >> origin: originPoint extent: extentPoint
    "Answer a rectangle with the given origin and size"

    <category: 'instance creation'>
    ^self basicNew origin: originPoint corner: originPoint + extentPoint

正如已经指出的那样,有一个basicNew可以阻止任何initialize构造函数手动运行或通过如上所示的类方法运行。

如果需要,您可以做些什么重写。您将创建自己的矩形类,该类将从Rectangle继承并将其重写。

例如:

Rectangle subclass:#ApplicationRectangle
        instanceVariableNames:''
        classVariableNames:''
        poolDictionaries:''
        category: ''

您将在哪里定义:

ApplicationRectangle class >> origin: originPoint extent: extentPoint
    "Answer a rectangle with the given origin and size"

    <category: 'instance creation'>
    ^self new origin: originPoint corner: originPoint + extentPoint

然后您将其命名为:

ApplicationRectangle origin: 5@5 extent: 40@30