在Pharo或Smalltalk中创建构造函数

在Pharo或Smalltalk中创建构造函数,smalltalk,pharo,Smalltalk,Pharo,我想在Pharo中实现一个小类,所以我做了以下工作: Object subclass: #Person instanceVariableNames: 'name age' classVariableNames: '' category: 'Test' 我想模拟一个构造函数,所以我制作了一个如下的方法: newName:aName newAge:aAge "comment stating purpose of message" name:=aName.

我想在Pharo中实现一个小类,所以我做了以下工作:

Object subclass: #Person
    instanceVariableNames: 'name age'
    classVariableNames: ''
    category: 'Test'
我想模拟一个构造函数,所以我制作了一个如下的方法:

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

    name:=aName.
    age:=aAge.
    ^self.
但当我想在法罗的操场上这样称呼它:

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

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

表达式
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指的是类,因为方法位于类一侧。但是由于
person
person
的实例,因此必须在实例侧定义消息
name:aName
age:anAge

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

Person >> name: aString
  name := aString

Person >> age: anInteger
  age := anInteger
使用这三种方法,您应该能够运行您的示例


关于编码样式的一些附加注释:

首先,我会为“构造函数”方法选择一个不同的选择器(在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

返回的新实例,尽管我大体上同意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.
通过这种方式,您可以控制是否公开属性

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


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

这一点很好。但是,我会使用
#name:age:
选择器而不是
#setName:age:
。其他语言需要在setter和getter前面加上
set
get
,因为它们没有其他方法来区分它们。在Smalltalk中,冒号是选择器的一部分,然后使用这些前缀就变得多余了。@LeandroCaniglia这正是它加前缀的原因-以区别于常规setter(不加前缀)。您不需要再次调用此方法。我重视拥有实例端构造函数的想法,以及将它们与常规setter进一步区分开来的想法。但是,我更喜欢传达方法分类的区别,而不是将
set
附加到选择器。使用
set
作为前缀是不好的,因为这只是一种隐藏事实的方式,即您除了。。。设置变量。在这种情况下,我会使用#initializeName:age:作为选择器。。。然后你可以这样做:
#name:aName-age:aNumber^self-basicNew-initializeName:aName-age:aNumber。
总之,这只是一个关于风格的讨论,而不是最重要的问题(问题本身)。。。所以我认为Leandro和Peter的回答都是正确的:)