When your average software developer goes to break a subsystem or module into implementable pieces, generally her first instinct is to think “What are the classes and behaviours in my problem domain?” Whether you’re driving this design with TDD, or taking a more top-down approach, this question tends to be the linchpin of the design.
This is unsurprising. Object-orientation is (ostensibly) taught to most of us in school as the one-true-way of doing things. It’s rare to find people who do not equivocate software-design-in-the-small to object-oriented design.
A couple of years ago, I decided that I wanted to learn “functional programming”. My first serious attempt was in OCaml. However, once the wheels hit the ground, I felt like I had returned to highschool. At that time, I knew the syntax and vocabulary surrounding objects, but I had no idea how I was supposed to use them.
So, I reasoned, I needed to learn how to design functional programs. I needed to learn “functional design”, for lack of a better term.
After a bit of consideration, I remembered that there are a lot of very famous universities that teach the basics of program design using Scheme. (This is also true for OCaml, but the Scheme ones are arguably more famous
.) This was perfect — I needed to learn how to design programs all over again, in a “functional style”. So, why not go back to basics? And thus did I seek out MIT’s CS1 old-style curriculum and the Wizard Book — also known as The Structure and Interpretation of Computer Programs (SICP). (They’ve since switched to Python, I think. I imagine the spirit is the same.)
You may notice that I’ve been putting “functional design” and “functional style” in quotations. The reason for this is that I quickly discovered that such things do not actually exist.
Let me explain.
The fundamental exploration that jumped out at me in SICP was this: Let’s say you have a programming model that supports first-class functions, and does a pretty good job of supporting immutable data. What reasonable methods of designing and implementing programs can we arrive at using this model?
And the answers are legion. A program can be viewed as a mathematical function. It can be viewed as a signal-processing device over finite sequences of discrete signal elements. It can be viewed as a signal-processing device over infinite sequences of discrete signal elements. It can be viewed as a processor of a domain-specific language. And so on.
What SICP really shows you is a variety of methods that you can use to design programs-in-the-small, with object orientation being only one of them. So now, when I’m working on a new chunk of some big project, I find myself thinking not “where are the objects”, but instead “what happens if I view this problem as, say, a signal processing problem?” Even more importantly, when I read someone else’s code, I can ask myself the same thing.
The reflex of explicitly asking myself this question when faced with a new design problem really helps to understand it from multiple angles, and to identify the things that are going to be the hardest to model or extend in any feasible design. And that lets me focus on clearing up the things that matter.
I won’t pretend to be a master of any design-in-the-small methodology. However, I’m now aware of a whole set of possibilities that I was once largely ignorant of, and this knowledge helps me in a bunch of little ways every day.