【问题标题】:Difference between instances created with and without `new` in GNU Smalltalk在 GNU Smalltalk 中使用和不使用“new”创建的实例之间的区别
【发布时间】:2019-08-14 23:49:04
【问题描述】:

有什么区别

Rectangle origin: 5@5 extent: 40@30

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

【问题讨论】:

  • 友情提醒考虑接受其中一个答案(或在 cmets 中要求进一步澄清)。

标签: smalltalk gnu-smalltalk


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    所以实际上将消息直接发送到类o手动创建然后设置它实际上是相同的

    【讨论】:

    • 我有一个子类Rectangle subclass: SvgRect,然后它突然变得不同了。使用new 它可以工作,没有它我会得到Object: SvgRect error: did not understand 错误。
    • @adius 这可能是因为另一个区别:除了#new,还有一个#basicNew,后者被#origin:extend: 类方法使用。 github.com/bonzini/smalltalk/blob/… basicNew 不调用初始化方法,因此您的“不理解”错误可能是因为您在使用类方法构造 SvgRect 实例时未初始化您在 SvgRect 中定义的附加状态。
    • 正如@JayK 所说,错误可能是因为 Rectangle 发送 basicNew 而不是 new,但它还必须有其他部分。如果我不得不猜测,我会说你的 SvgRect 已经定义了一个 initialize 方法,所以问题是使用类端 origin:extent: 将不起作用,因为与 new 不同,basicNew 不发送initialize 到对象。如果是这种情况,您可能需要将 SvgRect 中的 origin:extent: 重新定义为我在原始答案中写的相同以避免错误。
    • @JayK @melkyades 那么你能告诉我我需要改变什么来摆脱github.com/ad-si/svgtalk/blob/master/tests/SvgRectTest.st#L15中的new吗?
    • @adius: 你必须写(SvgRect origin: ... extent: ...) style: ...; yourself,所以消息#style: 不会发送到类,而是发送到新对象。否则我看不到任何陷阱。
    【解决方案3】:

    我认为重要的是要注意 Smalltalk 和其他 OO 语言之间的区别

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

    例如,在 ruby 中你会这样做

    # 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
    

    现在来看你的例子

    正如 JayKmelkyades 解释了主要区别,我将在这些方面给出更细微的区别(细节):

    第一:

    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
    

    【讨论】:

      猜你喜欢
      • 2021-02-08
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多