An old friend sent me a note recently asking for advice
about a problem he was having with Smalltalk. He explained
the situation well. It involved inconvenient binding of
super in an exception handler that was providing
the default behavior in a double dispatch. I didn't have
a slick fix, so I had to get philosophical ...
I worked through your problem and see what is going wrong.
I haven't faced the same thing and solved it so I have
nothing clever to offer. I have found my self caught
in similar fixes and here is what I've learned:
- Smalltalk has some sophisticated features that are so fundamental that they almost never let me down. They include: message dispatch, inheritance, garbage collection.
- Smalltalk also has some clever features that can be real timesavers when used sparingly and within proven idioms. Examples include: super, perform:, blocks, doesNotUnderstand:, value, processes, asSymbol, deepCopy, Behavior, weak pointers. (Can you add to this list?)
- Whenever I am surprised by Smalltalk's inability to do something, I eventually end up doing more of the former and less of the latter.
You know, I've also found that I can impress beginners by
dragging out one of Smalltalk's neat little features.
But, to impress Smalltalk pros I have to show them a
clever use of plain old objects.
I hope this helps should you have to yank the
machinery and replace it with something that, at first,
seems stupid and repetitive.
--
WardCunningham
This sounds a place to apply a pattern I have I'll call
"Refactor Problem Expressions".
I assume, from your description, that the problem method
contains an expression like:
"..."
super doSomethingWith: anArgument.
"..."
and "super" is binding to an inconvenient receiver.
I like to replace the "super" with an indirection, like
this:
"..."
self doubleDispatcher doSomethingWith: anArgument
"..."
Now, the implementation of "doubleDispatcher" can be
experimented with until you like it. Of course, there is
probably a better name than "doubleDispatcher" that you can
use.
The point of the pattern is to isolate the problem
in its own method that you can hack on while you find your
favorite implementation.
Furthermore, the existence of this problem usually means
that the original expression is hard for a reader to understand.
So I might refactor again, into something like this:
"..."
self doubleDispatchWith: anArgument.
"..."
and put this indirection in the "doubleDispatchWith:"
method, like this:
doubleDispatchWith: anArgument
^self doubleDispatcher doSomethingWith: anArgument
Now, you can rename "doubleDispatchWith:" into
something that names the operation you were trying to do,
and the code becomes even easier to read.
Most of the time, I find that the "extra" indirections have,
in fact, helped me identify and provide the seams and hooks
that should have been in the original method.
--
TomStambaugh