【问题标题】:making a constructor in Pharo or Smalltalk在 Pharo 或 Smalltalk 中创建构造函数
【发布时间】:2016-04-27 01:36:11
【问题描述】:

我想在 Pharo 中实现一个小类,所以我这样做了:

Object subclass: #Person
    instanceVariableNames: 'name age'
    classVariableNames: ''
    category: 'Test'

我想模拟一个构造函数,所以我做了一个如下的方法:

newName:aName newAge:aAge
    "comment stating purpose of message"

    name:=aName.
    age:=aAge.
    ^self.

但是当我想从 Pharo 的操场上这样调用它时:

objPerson:=Person newName:'Carl' newAge: 10.

Pharo 无法识别,我做错了什么?

【问题讨论】:

    标签: smalltalk pharo


    【解决方案1】:

    表达式Person newName: 'Carl' newAge: 10 是给类对象Person 的消息。因此,您必须在 Person 的类端实现它。

    您的代码需要像这样调整

    Person class >> newName: aName newAge: anAge
      | person |
      person := self new.
      person name: aName.
      person age: anAge.
      ^person
    

    请注意,在上面的代码中,self 指的是类,因为方法在类端。但是由于personPerson 的一个实例,所以消息name: aNameage: anAge 必须在实例端定义。

    因此,在实例端,需要添加两个setter:

    Person >> name: aString
      name := aString
    
    Person >> age: anInteger
      age := anInteger
    

    使用这三种方法,您应该能够运行您的示例。


    关于编码风格的一些额外的 cmets:

    首先,我会为“构造函数”方法选择不同的选择器(在 Smalltalk 中,我们将这些称为“实例创建”方法)。例如,

    Person class >> named: aString age: anInteger
      ^self new name: aString; age: anInteger
    

    其次,不需要为新创建的实例使用临时的person,因为表达式self new 已经引用了这样的实例。

    最后,注意中级联语法的使用

    ^self new name: aString; age: anInteger
    

    这意味着消息age: anInteger 将被发送到与name: aString 相同的接收者,在这种情况下恰好是self new 返回的新实例。

    【讨论】:

      【解决方案2】:

      虽然总的来说我同意 Leandro 的回答,但它将访问器暴露给私有属性。

      Kent Beck 在他的 Smalltalk Best Practice Patterns(我强烈推荐)中建议使用 set 作为实例端构造函数的前缀,例如:

      "add to class side to 'instance creation' protocol"
      Person>>class name: aName age: anAge
          ^ self new
              setName: aName age: anAge;
              yourself
      
      "add to instance side to 'initialization' protocol"
      Person>>setName: aName age: anAge
          name := aName.
          age := anAge.
      

      这样您可以控制是否公开属性。

      或者你可以坚持原来的命名,只是添加你忘记的new

      objPerson := Person new newName:'Carl' newAge: 10.

      【讨论】:

      • 好点。但是,我会使用#name:age: 选择器而不是#setName:age:。其他语言需要在 setter 和 getter 前面加上 setget,因为它们没有其他方法可以区分它们。在 Smalltalk 中,冒号是选择器的一部分,然后这些前缀的使用就变得多余了。
      • @LeandroCaniglia 这正是它加前缀的原因——将它与常规的 setter(没有前缀)区分开来。您不应该再次调用此方法。
      • 我重视拥有实例端构造函数的想法,以及将它们与常规设置器进一步区分开来的想法。但是,我更喜欢传达与方法分类的区别,而不是将set 附加到选择器。
      • 使用set 作为前缀是不好的,因为它只是隐藏你除了...设置变量之外什么都不做的事实的一种方式。在这种情况下,我会使用 #initializeName:age: 作为选择器...然后您可以执行以下操作:#name: aName age: aNumber ^ self basicNew initializeName: aName age: aNumber.
      • 无论如何,这只是关于风格的讨论,而不是最重要的(问题自己发布了)......所以我认为 Leandro 和 Peter 的答案都是正确的 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多