8
\$\begingroup\$

I'm new to GNU Smalltalk. I'd like to port a script I've written in Ruby, Scala, CoffeeScript, and several others. It's the one I use to try to learn the classic OOP concepts of abstract classes, abstract methods, overriding, dispatch, etc. It goes like this:

  1. Define an abstract class Animal with a constructor to set the animal's name, a speak method to return a string N says S where N is the name and S is the sound the animal makes.
  2. Define three concrete subclasses Horse, Cow, and Sheep, each with their own sound-returning string method.
  3. Create a horse and sheep and a cow, and make them speak. For the horse and cow, create a variable. For the sheep, call the speak method on an "anonymous" object.

My GNU Smalltalk solution is:

Object subclass: Animal [
    | name |
    Animal class >> new: n [^super new init: n]
    init: n [name := n]
    speak [^name, ' says ', self sound]
]

Animal subclass: Horse [
    sound [^'neigh']
]

Animal subclass: Sheep [
    sound [^'baaaa']
]

Animal subclass: Cow [
    sound [^'moooo']
]

h := Horse new: 'CJ'.
h speak displayNl.
c := Cow new: 'Bessie'.
c speak displayNl.
(Sheep new: 'Little Lamb') speak displayNl.

I get the expected output but I am unhappy with the solution. I'd appreciate input on:

  1. The whole new and init thing. Is there a way to make it cleaner?
  2. I noticed I did not have to override new or init in my subclasses; did the subclasses really inherit new? It is a classmethod and I don't know if it is inherited or not.
  3. Are we supposed to give an implementation of new? I had heard this was not a good idea. I know from Ruby that you just define initialize and use the built-in new to automatically invoke the initialize instance method.
  4. Is this GNU Smalltalk syntax I got from the GNU tutorial proper or is it an extension of some sort? Should this be written in a proper, "purer" Smalltalk style? I've seen some where there is an instanceVariables message.
  5. I know Smalltalk has something called subclassResponsibility. Is it wise to declare the sound method in the Animal class (as an abstract method)?
\$\endgroup\$
4
\$\begingroup\$

Item 1 Instead of Animal new: n use Animal named: aString implemented on the class side of Animal as:

named: aString
    ^self new name: aString

Then in the instance side of the class define

name: aString
    name := aString

Item 2 Yes, new is inherited by the subclass. The same happens with all class methods; they are inherited by subclasses.

Item 3 You don't have to implement new, unless you want to do something special in your class. In most dialects (I haven't checked GNU Smalltalk) new will call the instance method initialize, which by default does nothing. If you need to initialize you instances, then implement initialize as

initialize
     super initialize.
     "your specific code"

Item 4 Can't help on this one but yes, the usual message for creating a subclass includes a keyword for declaring named instance variables.

Item 5 You have two options here. (1) You can implement sound in Animal as self subclassResponsibility. (2) You can implement it with a default value ^oops. The first alternative will indicate the programmer extending the hierarchy what method they must to provide in the new subclass. The second will just provide a default sound for the programmer to find out and refine.

---EDIT---

A more realistic implementation (meaning, one not intended to test OOP concepts in Smalltalk) would get rid of the name instance variable of Animal. This is possible because the class name already provides the animal's name. In other words, the instance doesn't need to "remember" its name because its class already knows it. Here is the instance side code

name
    ^self class name asLowercase

Note that instances of the abstract class Animal will now answer the string 'animal' as their name.

With this simplification the instance creation method (class side) named: would no longer be required. However, we could tweak its implementation so it answers an instance of the appropriate subclass:

named: aString
    | subclass |
    subclass := Animal withAllSubclasses
        detect: [:class | class name asLowercase = aString] ifNone: [self].
    ^subclass new

With these changes we no longer need the name instance variable and also will be able to create, say an instance of Horse evaluating: Animal named: 'horse', etc.

\$\endgroup\$
  • \$\begingroup\$ This is a great answer, and the code did work when I implemented your suggestions. It does, however, introduce name: as a "setter" of sorts. I would like the name to be unchangeable after initialization. Is this possible while keeping the lovely named: class method? \$\endgroup\$ – Ray Toal Feb 8 '15 at 2:23
  • \$\begingroup\$ Good question. I've updated my answer. \$\endgroup\$ – Leandro Caniglia Feb 8 '15 at 11:56
  • \$\begingroup\$ In GNU Smalltalk, initialize does not run automatically on object creation. \$\endgroup\$ – ben rudgers Aug 29 '17 at 22:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.