Here's the original code:
elementsIn: anElement do: aOneArgBlock depthFirst: aBoolean
| left children |
left := OrderedCollection with: anElement.
[| next |
next := aBoolean ifTrue: [left removeLast] ifFalse: [left removeFirst].
(aOneArgBlock value: next) == false
ifFalse: [children := OrderedCollection new.
next elementsDo: [:each | aBoolean ifTrue: [children addFirst: each] ifFalse: [children addLast: each]].
left addAll: children]]
YUCK! What was I thinking with two checks for depthFirst or not? Here's the new code (which is a new class by the way!):
pvtDo: doBlock depthFirst: isDepthFirst
| searchQueue dyadic |
dyadic := self pvtCurryForContinue: doBlock.
searchQueue := OrderedCollection withAll: self pvtChildren.
[searchQueue isEmpty] whileFalse:
[| next continue |
next := self class on: searchQueue removeFirst.
continue := [next addChildrenTo: searchQueue depthFirst: isDepthFirst].
dyadic value: next value: continue]
Now, you might notice some weirdness like the #pvtCurryForContinue:. All it does is to call the continue block by default if the block only accepts one argument. Otherwise, it let's the block continue through. Here's it's implementation:
^doBlock numArgs == 1
[[:each :continue |
doBlock value: each.
This is my functional programming training shining through. This code is much cleaner and easier to read than the one before.
This was just one simple example. I used the Thesaurus project to refactor to see how enforcing private members in Smalltalk would feel. So far, I found a lot of breaches of encapsulation that I had not noticed in the heat of development. Even with categorizing methods as private and with comments, I still broke the boundaries. The pvt at the beginning makes it obvious and Squeak's compiler tells me immediately when I have done something wrong.
After the refactoring, I felt my design was much cleaner overall. So far, I'm enjoying using pvt at the beginning of my private methods. It makes me get into the habit of "telling" my objects instead of "asking" which I generally do well. But, it helps when I'm weak in the rush to get something done or simply not as focused as I should be. The public protocol is obvious and minimal. Plus, if I have too many pvt methods, I start looking through my methods to break the object down further.