Object-oriented decomposition -- why and why not
My friend Nels Beckman is writing on how to do functional programming in Java. He asked me why I thought people build systems that follow an OO decomposition. Since my boss has been relentless about getting the book done, I have not posted a blog recently, so here’s my note to Nels.
When you decompose systems, at some point you have a chunk (module, component, …) that you want to structure internally. How do you do that? One of the strategies is to mirror domain concepts. This actually works fine in most IT cases (and probably most others too, once you get used to it). There is an argument that the domain concepts (nouns) change very slowly, while the their behaviors seem to always be evolving (verbs). So if you align your structure with the nouns you get less churn induced by the domain changing. This OO strategy implicitly promotes modifiability.
Your suggestion to use a functional programming style is similar to the “orthogonal abstraction” I describe in the book — in the functional case it is usually some math formalism, hence the desire to work with tuples instead of a domain-specific concept. I can see two big advantages to the orthogonal abstraction. First, it could be much faster (or some other quality attribute). In fact it would be surprising if the OO strategy was the fastest — why would the way things are “in real life” naturally align with performance? And second, it could be that there is a domain that has already been well studied (e.g. compilers, databases, static analysis) that has its own set of (stable) abstractions. Experts have decided that these are the essential concepts and they work well.
A day in the life of an IT programmer is a frustrating exercise in what is not known. It is not about engineering a great solution to a known problem. Perhaps this is an under-appreciated fact in academics, and it would explain many prejudices. You’re just some guy trying to make the system do what the marketing / sales / customers want it to do. You never understand the domain (banking, inventory, insurance, etc.) as well as the experts. You are always building an approximation of what they’d really like to have. They always wish it was already built and you always build it too slowly. What you built yesterday doesn’t really support what they want tomorrow. I think systems/OS guys feel less like this — they have less churn in their domain and they spend more time working on optimizing the internal implementation of a module/component.
There’s a story about building a system to track boats on a lake. The first design is an OO one, with classes representing boats, trips, and fares; it can tell you what the cash register should hold at the end of the day. The second design is minimal: just keep two counters that sum the departure and return times of each boat. At the end of the day, subtract the departure counter from the return counter to get total minutes and multiply by rate. Ta da. But if you ask for any change to the OO system (say discount fares after 5pm) it’s easy to add because the change is relative to the domain, which is already encoded in the design, while the other solution has pruned the problem to its core, so you’d have to start over to change anything. I’m not saying this story is accurate — it’s a fable so it’s exaggerated — but the nugget of truth is there, including the extreme simplicity of non-OO solutions. (If anyone knows the origin if this fable please let me know).