Reputation: 14962
How can I have a class expose the same method with 2 different names?
E.g. that the asDescripton
function does the same thing / re-exports the asString
function without simply copy-pasting the code.
Object subclass: Element [
| width height |
Element class >> new [
^super new init.
]
init [
width := 0.
height := 0.
]
asString [
^ 'Element with width ', width, ' and height ', height.
]
asDescription [ "???" ]
]
Upvotes: 2
Views: 154
Reputation: 14858
In Smalltalk you usually implement #printOn:
and get #asString
from the inherited version of it which goes on the lines of
Object >> asString
| stream |
stream := '' writeStream.
self printOn: stream.
^stream contents
The actual implementation of this method may be slightly different in your environment, the idea remains the same.
As this is given, it is usually a good idea to implement #printOn:
rather than #asString
. In your case you would have it implemented as
Element >> printOn: aStream
aStream
nextPutAll: 'Element with width ';
nextPutAll: width asString;
nextPutAll: ' and height ';
nextPutAll: height asString
and then, as JayK and luker indicated,
Element >> asDescription
^self asString
In other words, you (usually) don't want to implement #asString
but #printOn:
. This approach is better because it takes advantage of the inheritance and ensures consistency between #printOn:
and #asString
, which is usually expected. In addition, it will give you the opportunity to start becoming familiar with Streams
, which play a central role in Smalltalk.
Note by the way that in my implementation I've used width asString
and heigh asString
. Your code attempts to concatenate (twice) a String
with a Number
:
'Element with width ', width, ' and height ', height.
which won't work as you can only concatenate instances of String
with #,
.
In most of the dialects, however, you can avoid sending #asString
by using #print:
instead of #nextPutAll:
, something like:
Element >> printOn: aStream
aStream
nextPutAll: 'Element with width ';
print: width;
nextPutAll: ' and height ';
print: height
which is a little bit less verbose and therefore preferred.
One last thing. I would recommend changing the first line above with this one:
nextPutAll: self class name;
nextPutAll: ' with width ';
instead of hardcoding the class name. This would prove to be useful if in the future you subclass Element
because you will have no need to tweak #printOn:
and any of its derivatives (e.g., #asDescription
).
Final thought: I would rename the selector #asDescription
to be #description
. The preposition as
is intended to convert an object to another of a different class (this is why #asString
is ok). But this doesn't seem to be the case here.
There is a reason why #asString
is implemented in terms of #printOn:
, and not the other way around: generality. While the effort (code complexity) is the same, #printOn:
is clearly a winner because it will work with any character Stream
. In particular, it will work with no modification whatsoever with
FileStream
)SocketStream
)Transcript
In other words, by implementing #printOn:
one gets #asString
for free (inheritance) and --at the same time-- the ability to dump a representation of the object on files and sockets. The Transcript
is particularly interesting because it supports the Stream
protocol for writing, and thus can be used for testing purposes before sending any bytes to external devices.
In Smalltalk, the goal is to have objects whose behavior is simple and general at once, not just simple!
Upvotes: 5
Reputation: 3141
As lurker wrote in the comments, send the asString message in asDescription.
asDescription
^ self asString
This is usually done to expose additional interfaces/protocols from a class, for compatibility or as a built-in adapter. If you create something new that does not have to fit in anywhere else, consider to stick to just one name for each operation.
Edit: if you really are after the re-export semantics and do not want the additional message sends involved in the delegation above, there might be a way to put the CompiledMethod of asString in the method dictionary of the class a second time under the other name. But neither am I sure that this would work, nor do I know the protocol in GNU Smalltalk how to manipulate the method dictionary. Have a look at the documentation of the Behavior
class. Also, I would not consider this as programming Smalltalk, but tinkering with the system.
Upvotes: 2