Prologue
It all started because I just wanted to play with a two text editor prototype. So I threw the following code (or something close to it, couple superfluous things removed) in a workspace:
window := ApplicationWindow new.
p := Panel new.
p beVerticalSpread.
p add: (TextEditorView model: 'Hello' asValue).
p add: (TextEditorView model: 'Wordl' asValue).
window component: p.
window open
It opened. But sadly, I couldn't enter any text into it. I kinda knew this in the back of my mind. And that there was some more "hooking up" I needed to do. After looking around, I added the following two lines:
window keyboardProcessor: KeyboardProcessor new.
p components do: [:each | window keyboardProcessor addKeyboardReceiver: each].
Someone Else's Wisdom
A while ago, I sat down one day to play with either Haskell or ML, I forget which. Functional Languages had been much talked about at the time quite a bit, so I got out a tutorial, and played around a bit. I didn't last long, so easy to want to learn something and then be distracted by something else. In the process, there was a bit of "philosophy" which has stuck with me to this day. I don't remember it exactly of course, but will paraphrase how I remember it.
This language has no assignment operator. 90% of all program errors are related to state. This language's solution to that is that it has no state. You don't ever store anything. At first, this total eschewing of state may seem a bit extreme. Think of it like a tibetan monk sequestered in a monastery. Going completely without state may seem harsh at first, but once we get rid of all the state in our programs, new avenues open up."
Or something like that. I'm butchering it. But for me, it helped codify something that growing experience had been whispering to me over time. The less state you keep in a program, the better. I'm still happily writing Smalltalk, even after playing the functional game a couple times, but it has very much influenced me to keep a sharp eye out for redundant data, to accept the responsibility of maintaining it when I employ caching, to basically try to have as few instance variables as possible.
KeyboardProcessor: Everyone wants a piece of the family jewels
ApplicationWindow holds a keyboardProcessor (though it's not initialized). An ApplicationWindows eventDispatcher will store one too, it just lazy initializes it from its window though. UIBuilders keep one. Though they keep a window too. And numerous widget controllers have an instance variable for one. With a setter, and lots of direct references. And this means that lots of builder code creates widgets, then hands them a keyboardProcessor:, the same one they're going to hand to the containing window. And lots of one off window construction methods repeat the same. Build the widget tree, then let widgets know what one of the instance variables of their topComponent is. And if you don't do this... the widgets don't respond to keyboard.
I decided to gamble and be courageous. I wondered if I could just turn all those controller keyboardProcessor references into:
self topComponent ifNotNil: [:window | window keyboardProcessor]
I stubbed in these methods on VisualPart/Controller and went to work. It was a common refactoring. Abstract references to the instance variable. Use the rewrite tool to have all callers of keyboardProcessor1 just be keyboardProcessor (from the abstract classes). Remove the local setter/getter. Remove the instance variable. Open a new browser, make sure all is well still.
Results
It worked. It's always so cool to me that I can rewrite things like list views and text editors in the same memory space as the one I'm modifying, and it all keeps working. I don't have a removed method count. There's one less instance variable in probably near 30 classes. And probably 20 or so removed methods. A few rewritten ones. One added one. Lots of methods with one or two lines less code. All that less... and now my original example works. It's so cool when removing stuff makes things work.