Noel Rappin Writes Here

Another Entropy Essay #3: Flavors of Object-Oriented Design

Posted on June 16, 2020

Who doesn’t love a taxonomy?

Previously on Locally Sourced: We talked about test speed, and we asked why you hired that test? If you like this kind of thing, tell a friend or colleague.

One thing that I see a lot in online discussion of programming styles is the idea that Object-Oriented Programming is just one thing that you either do, or don’t do.

I think that’s reductive, and not just because different languages encourage different structures in objects. There are a lot of different kind of design that people use that we still call “Object-Oriented Programming”, and I don’t think I’ve ever seen a good set of terms for the various sub-techniques. So I decided to see if I could create some.

Backing up for a second. What makes a program “Object-Oriented” is that the code is split into objects, where each object has data and methods that act on that data. Each object maintains its own state and the code flow is mainly objects sending messages to each other to query about and change their state.

I think it’s worth trying to separate out two different dimensions of the way Object-Oriented programs are built so that we can talk about the benefits and costs of different approaches.

I’ve been trying to come up with names for the approaches that don’t have value judgements associated, because I think there are reasonable arguments to be made for a lot of different points on these axes.

Syntactic vs. Semantic

The first axis is structural, and I’m calling the ends “Syntactic” vs “Semantic”.

A “Syntactic” OO design uses the language features of “class” and “attribute” and the data encapsulation that goes along with that without using the OO system to drive the workflow.

A “Semantic” OO design uses Object-Oriented patterns more aggressively, in particular using polymorphism to drive workflow. Other Semantic structures might include:

  • Using polymorphic code to avoid conditionals.
  • Using the null object pattern to avoid null checks.
  • Dependency injection, meaning generating class references and therefore behavior at runtime.

Technical vs. Domain

The other axis is modeling and the distinction is between “Technical” and “Domain”

In Technical modeling, the names of classes tend to mirror system concepts like, well, “system”, “action”, “workflow”, or “service”. In Domain modeling, names of classes tend to mirror real-world objects in the domain: “mail carrier”, “checkout”, “adjuster”, “invoice”, “sale”. Obviously there’s a continuum here, most Technical systems will still have some Domain concepts, even if just a User, but it’s worth focusing on where the business logic is and how that logic is modeled.

I’m pretty sure that you can live in any of the four quadrants here. You might have a Domain model like “Box Office”, but still have it be a big class with a lot of conditionals. You could also have a Technical model name like “TicketSaleWorkflow” but have it paired with a lot of NullTicket objects or GuestUserSale objects that would have more of a Semantic feel.

Left to my own devices, I tend toward the Semantic (I really like null objects and polymorphic structures, though I’ve never taken to dependency injection), and the Technical (which I think is a legacy of being a consultant in ways I’ll get to in a second).

When Would You Use Each One?

Now we can start to talk about trade-offs.

You probably don’t expect me to defend the Syntactic style of OO, but I think in the right circumstances it does have a lot going for it. Syntactic OO systems tend to be less complicated. They also tend to be easier to explain, especially to developers who don’t have a lot of experience reading Semantic OO. A common critique of heavily Semantic OO is that it’s hard to find where anything actually gets done.

On the other hand, Semantic OO is, at least in theory, cheaper to change because there are typically smaller units of code and typically lots of interactions that can easily have new code inserted, through polymorphism or dependency injection or what have you. Semantic OO also tends to be easier to test. (It’s the combination of “easier to test” and “hard to find where things get done” that was the target of the DHH “TDD is Dead” talk.)

I think the Technical vs. Domain distinction is a bit more a matter of taste. (Though there is a tension between Domain and Semantic, in that if you push Semantic far enough, you tend to get objects like NullTicketOperator that aren’t really part of the domain). Looking back at my mid 90s OO textbooks, I had forgotten how strongly domain modeling was included as the essential ingredient in OO programming. “Look at your requirements and find the nouns” was considered the first step.

Technical modeling has both the advantage and the disadvantage of being generic. When I have been working as a consultant, and building new apps relatively frequently with a small team, generic modeling is great. Nearly every app can be deconstructed into things like “service”, “action”, and “workflow”, and if those don’t give you quite as much leverage on the problem as using your domain nouns, it’s much easier to develop skills that allow you to deconstruct a problem across multiple domains.

So. Semantic vs. Syntactic and Technical vs. Domain. What about your projects? What flavor of OO do you use? Does it seem to suit the team, the language, and your goals?


comments powered by Disqus

Copyright 2024 Noel Rappin

All opinions and thoughts expressed or shared in this article or post are my own and are independent of and should not be attributed to my current employer, Chime Financial, Inc., or its subsidiaries.