Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

making a constructor in Pharo or Smalltalk

I want to implement a small class in Pharo so I have done this:

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

and I want to simulate a constructor so I have made a method like the following:

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

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

but when I want to call it from the playground in Pharo like this:

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

it is not recognized by Pharo, what am I doing wrong?

like image 732
Little Avatar asked Dec 07 '22 22:12

Little


2 Answers

Although in general I agree with Leandro's answer, it exposes accessors to the private properties.

Kent Beck in his Smalltalk Best Practice Patterns (which I highly recommend), proposes to use set as a prefix for the instance-side constructor, e.g.:

"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.

This way you can control whether you will or will not expose the properties.

Or you could stick with your original naming and just add new, which you forgot.

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

like image 27
Peter Uhnak Avatar answered Feb 02 '23 00:02

Peter Uhnak


The expression Person newName: 'Carl' newAge: 10 is a message to the class object Person. Therefore you have to implement it on the class side of Person.

Your code needs to be tweaked like this

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

Note that in the code above self refers to the class because the method is on the class side. But since person is an instance of Person, the messages name: aName and age: anAge have to be defined on the instance side.

Therefore, on the instance side, you need to add the two setters:

Person >> name: aString
  name := aString

Person >> age: anInteger
  age := anInteger

With these three methods you should be able to run your example.


Some additional comments on the coding style:

Firstly, I would have chosen a different selector for the "constructor" method (in Smalltalk we call these "instance creation" methods). For instance,

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

Secondly, there is no need to use the temporary person for the newly created instance as the expression self new already refers to such an instance.

Finally, note the use of the cascade syntax in

^self new name: aString; age: anInteger

which means that the message age: anInteger will be sent to the same receiver that name: aString, which in this case happens to be the new instance returned by self new.

like image 135
Leandro Caniglia Avatar answered Feb 02 '23 00:02

Leandro Caniglia