patternMinor
Polymorphic animals speak their name and make a noise
Viewed 0 times
polymorphicspeakmakenamenoiseandanimalstheir
Problem
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:
My GNU Smalltalk solution is:
I get the expected output but I am unhappy with the solution. I'd appreciate input on:
- Define an abstract class
Animalwith a constructor to set the animal's name, aspeakmethod to return a stringN says SwhereNis the name andSis the sound the animal makes.
- Define three concrete subclasses
Horse,Cow, andSheep, each with their own sound-returning string method.
- 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:
- The whole
newandinitthing. Is there a way to make it cleaner?
- I noticed I did not have to override
neworinitin my subclasses; did the subclasses really inheritnew? It is a classmethod and I don't know if it is inherited or not.
- 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 defineinitializeand use the built-innewto automatically invoke theinitializeinstance method.
- 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
instanceVariablesmessage.
- I kno
Solution
Item 1
Instead of
Then in the instance side of the class define
Item 2
Yes,
Item 3
You don't have to implement
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
---EDIT---
A more realistic implementation (meaning, one not intended to test OOP concepts in Smalltalk) would get rid of the
Note that instances of the abstract class
With this simplification the instance creation method (class side)
With these changes we no longer need the
Instead of
Animal new: n use Animal named: aString implemented on the class side of Animal as:named: aString
^self new name: aStringThen in the instance side of the class define
name: aString
name := aStringItem 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 asinitialize
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 codename
^self class name asLowercaseNote 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 newWith 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.Code Snippets
named: aString
^self new name: aStringname: aString
name := aStringinitialize
super initialize.
"your specific code"name
^self class name asLowercasenamed: aString
| subclass |
subclass := Animal withAllSubclasses
detect: [:class | class name asLowercase = aString] ifNone: [self].
^subclass newContext
StackExchange Code Review Q#75307, answer score: 4
Revisions (0)
No revisions yet.