在Pharo或Smalltalk中制作构造函数

Lit*_*tle 4 smalltalk pharo

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

Object subclass: #Person
    instanceVariableNames: 'name age'
    classVariableNames: ''
    category: 'Test'
Run Code Online (Sandbox Code Playgroud)

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

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

    name:=aName.
    age:=aAge.
    ^self.
Run Code Online (Sandbox Code Playgroud)

但是当我想在Pharo的操场上打电话时,就像这样:

objPerson:=Person newName:'Carl' newAge: 10.
Run Code Online (Sandbox Code Playgroud)

Pharo不承认它,我做错了什么?

Lea*_*lia 6

表达式Person newName: 'Carl' newAge: 10是类对象的消息Person.因此,你必须在类的一边实现它Person.

你的代码需要像这样调整

Person class >> newName: aName newAge: anAge
  | person |
  person := self new.
  person name: aName.
  person age: anAge.
  ^person
Run Code Online (Sandbox Code Playgroud)

请注意,在上面的代码中self引用了类,因为该方法在类的一侧.但是,由于person是的一个实例Person中,消息name: aNameage: anAge必须在实例侧限定.

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

Person >> name: aString
  name := aString

Person >> age: anInteger
  age := anInteger
Run Code Online (Sandbox Code Playgroud)

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


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

首先,我会为"构造函数"方法选择一个不同的选择器(在Smalltalk中我们称之为"实例创建"方法).例如,

Person class >> named: aString age: anInteger
  ^self new name: aString; age: anInteger
Run Code Online (Sandbox Code Playgroud)

其次,没有必要将临时person用于新创建的实例,因为表达式self new已经引用了这样的实例.

最后,请注意使用级联语法

^self new name: aString; age: anInteger
Run Code Online (Sandbox Code Playgroud)

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


Pet*_*nak 6

虽然总的来说我同意Leandro的答案,但它暴露了私有财产的访问者.

Kent Beck在他的Smalltalk最佳实践模式(我强烈推荐)中,建议将其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.
Run Code Online (Sandbox Code Playgroud)

这样,您可以控制是否要公开属性.

或者您可以坚持使用原始命名并添加new,这是您忘记的.

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

  • 使用`set`作为前缀是不好的,因为这只是一种隐藏你没有做任何事情但除了......设置变量的事实.在这种情况下,我会使用#initializeName:age:作为选择器...然后你可以这样做:`#name:aName age:aNumber ^ self basicNew initializeName:aName age:aNumber. (2认同)