On the recommendation of a number of Rubyists, I read Kent Beck’s Smalltalk Best Practice Patterns (SBPP), where I found a lot of helpful principles for structuring object-oriented code. Now that I’ve started getting some initial experience working in a Smalltalk development environment (specifically Squeak), it seems to me that one of the factors that led to the emergence of the patterns in SBPP was the unique nature of the Smalltalk development environment itself. This isn’t to diminish the insights of Beck, Ward Cunningham, and their Smalltalker colleagues, but only to highlight the role that the environment played as well.
Let’s take a look at a key feature of the Smalltalk development environment, the browser. We’ll look at how the browser works and how I would suggest it impacts the design of Smalltalk code. We’ll end by considering how we can apply what we’ve learned to promoting software design in other languages.
The Browser
Instead of storing code in source files, Smalltalk stores classes in memory. The entire state of the development environment is persisted out in an image file. And this is where the differences begin. When code is stored in text files, the most natural way to display that code is to display the text of the file. Any alternate ways to display the contents, such as lists of methods, are supplemental. One reason for this is that you need a way to display arbitrary contents the developer has added at any point in the file, such as a comment in the middle of nowhere.
By contrast, when classes are stored in-memory, there is no predetermined way to display them; the environment can choose a way to display them that makes the most sense. And for Smalltalk, the way that’s been chosen is the browser.
At the top it shows a multi-panel interface allowing you to browse through class categories to classes to method protocols to methods. When you select something it’s shown in the bottom of the window: a class declaration or a method definition. In the browser, the list of methods isn’t supplemental: it’s the whole way you access the code.
The Impact
Communicative message signatures: because only one method definition is shown at a time in a browser, you don’t automatically have the other method definitions in the class in front of you. So it’s more work to look up the definition of another method. But you can easily see the signatures of other methods in the browser at a glance. As a result, there is incentive to name methods in such a way that it’s clear from their name what they do, and you don’t usually need to look up the implementation. This encourages interface-based thinking. By contrast, in source files (at least modern ones that don’t have separate “header/interface” files), the implementation is visible right next to the interface and takes up the most space, so this discourages interface thinking.
Short methods: Because you’re only viewing one method at a time, it could very well fit on a single screen. As a result, there is incentive to keep your methods short enough that they do fit on a single screen. What’s more, browser windows by default aren’t very tall (probably due to historic screen sizes), and you don’t have the full window height for code: the top half is the browser multi-panel interface and only the bottom half is for code. So a method that is short enough to fit on the screen in a default browser window will be quite short indeed. By contrast, when a class is stored in a text file with all its methods, all but the smallest classes won’t fit on a single screen, so there is less incentive to get individual methods to fit on a screen either.
Full-featured classes: Because class methods are grouped into protocols within a class, there is more incentive to add more methods to a class. In most languages, which don’t group methods, adding a lot of methods can make it hard to navigate through the class to find the method you’re looking for. But navigating through methods within protocols is easy in the browser. As a result, Smalltalk classes can end up very full-featured and self-contained, but the downside is that they can end up with many responsibilities.
Code reuse: Smalltalk doesn’t have private methods; all methods are publicly accessible. By convention, the private
protocol is used for messages that should only be sent to an object by itself. But because there’s a single private protocol for the multiple other protocols you use for public methods, there’s incentive not to make too many methods private. As a result, more methods are exposed publicly for other code to take advantage of. This results in more opportunities for code reuse, as the small methods are usable elsewhere.
Extending classes: in Smalltalk, code isn’t stored in source code files. The classes exist in memory, you browse them, and you can change them. And you add one method at a time. As a result, when you have a new method to add, there is little difference between adding it to your own class and to a pre-existing class, even a Squeak system class. And protocols allow you to organize the methods you’ve added to classes to distinguish them from pre-existing methods. As a result, there’s more incentive to add methods to whichever object would be most convenient to send a message to. Smalltalk isn’t the only language to allow you to “re-open” classes to add methods to them is possible—for example, Rails’ Active Support library adds methods to Ruby’s Array
, String
, and even Object
. But in Ruby you have to create a source file that re-opens the class and adds the method, which is a bit more work and draws attention to the fact that you’re doing something atypical. Usually you fully define a class in one place, but here you’re re-opening the class. This is referred to in Ruby as “monkey-patching” and usually comes with a lot of warnings. In Smalltalk, there isn’t a word for it: it’s just how you always create a method.
Changing How You See
Realizing how Smalltalk’s development environment influenced its code design changes the way we think about those patterns. If Smalltalkers follow these patterns better than Rubyists or developers of other OO languages, it’s not necessarily because the Smalltalkers are more disciplined. Some things are automatic in the Smalltalk environment that developers in other environments need to choose to do, as a discipline. This doesn’t mean these things shouldn’t be done in other languages, but it does mean that the cost of doing so is higher.
And this means that if we want to promote the kinds of code structure practices in SBPP, the best way isn’t to tell people to be more disciplined, but to create programming environments that foster those practices the way Smalltalk does. But Smalltalk’s image paradigm is pretty different than the source-file paradigm that is almost universal in professional software development today. It remains to be seen if market forces have space for a development environment like Smalltalk’s.