Squeak
  links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Case History: Adding a Method to an Existing Class
Last updated at 4:54 pm UTC on 16 January 2006
This is a blow-by-blow description of adding functionality to the image.

Goal: I wanted to print irregular fractions as a mixed number. That is, I wanted to print (5/3) as 1(2/3).

The first step was to look at class Fraction and find out what functionality is already present. Looking at the protocol we find a "truncated" method. So, in a Workspace let's try:

 (5/3)truncated 
and print-it using the command-p keystroke. We get:

 (5/3)truncated        1 
and
 (25/3)truncated       8
So, now we have a way to get the whole number from an irregular fraction.

Next, we need to return a string representing the mixed number. Under the World menu | open... submenu we find a choice "message names (W)".

This brings up way to search for messages that contain a given string. Since we want to print a string let's try "printString". This returns a list of methods which we can browse. I was interested by Symbol printStringWithPound which is implemented as:

 ^'#', self printString.
Using what I've learned so far here is my first design:

 printStringAsMixedNumber
 	| wholePart fractionPart text |
 	wholePart _ self truncated.
 	fractionPart _ self - wholePart.
        text _ '' , wholePart printString , fractionPart printString.
	^ text.
After coming up with my first design I turned it loose on the squeak-dev list and Goran Hultgren responded:

Subject: Re: [ENH][newbie] Fraction >> printStringAsMixedNumber
Date: Wed, 6 Mar 2002
From: goran.hultgren@bluefish.se
To: squeak-dev@lists.squeakfoundation.org

> Michael Maloney wrote:
> Well, all the books have said to start small, so . . .

Exactly! After all - it is SMALLtalk. :-)

> In case anyone else finds it useful, here is a method I've added to Fraction.
>
> "(5/3) printStringAsMixedNumber" prints as 1(2/3).
>
> The newbie part is: Have I followed accepted practice as far as naming convention and putting the method in the printing protocol?

Think so. I might have named it #printAsMixedNumber, but that is mere taste.

> Also, (especially) since I modified a kernal class, should I have named it something else?

Nope, seems fine to me. A few bits of advice:

1. Learn to use ChangeSets instead of .st files. They are a bit more useful.

2. When adding "printYaddaYadda" methods I usually tend to prefer the form that takes a stream as an argument (as #printOn:) - it can make the method a bit more useful. More on that below.

3. Your line that goes:

	text _ '' , wholePart printString , fractionPart printString.
...can instead be written as:

	text _ wholePart printString , fractionPart printString.
There is no need to concatenate with an empty String at the beginning. I am not sure why you did that.

Ok, follow me on a little "excursion" here - this is not meant as criticism - hopefully there is something to be learned from it. Let's change the method a teeny bit first:

 printStringAsMixedNumber
	| wholePart fractionPart |
	wholePart _ self truncated.
	fractionPart _ self - wholePart.
	^wholePart printString , fractionPart printString
The return is always "evaluated" last so you can always safely put it at the beginning of the line and you don't have to use parentheses.

You also don't need to end the final statement in the method with the .-character. It is a "statement separator" not a "statement endmarker".

On the other hand, the period can be there without errors.

Let's go a bit further:

 printStringAsMixedNumber
	| wholePart |
	wholePart _ self truncated.
	^wholePart printString , (self - wholePart) printString
When examining the implementation of #printString (select it and press Alt-m) we see that it is actually a "limited" version. In this case perhaps #fullPrintString would be better - at least we get rid of two extra message sends! :-) (Yeah, yeah, premature optimization is the root of all evil which Donald Knuth didn't say btw).

So currently we have:

 printStringAsMixedNumber
	| wholePart |
	wholePart _ self truncated.
	^wholePart fullPrintString , (self - wholePart)
                                       fullPrintString
...if we then take a look at fullPrintString (Alt-m) we find it implemented like this:

 ^String streamContents: [:s | self printOn: s]
The method streamContents: is kind of new in Smalltalk I think. I just checked the versions of it (Alt-v) but I found no reference to when it was introduced. It is a nice method though and makes it easy to produce a String by using methods that wants to write on a stream.

Hmmm. So then we might end up with my suggestion for a stream-based implementation like this:

 printAsMixedNumberOn: aStream
	| wholePart |
	wholePart _ self truncated.
	wholePart printOn: aStream.
	(self - wholePart) printOn: aStream
This method takes a Stream as an argument and writes the representation on the stream. The nice part with this is that the Stream can be any kind of Stream like for example a FileStream.

...and if you still want a method returning a String you can always add an extra method like this:

 printStringAsMixedNumber
	^String streamContents: [:stream | self printAsMixedNumberOn:
                                          stream]
And here ends my little excursion. :-)

> Cheers,
> Mike

Cheers, Göran

To be Continued...