The Ultimate OOP design principles

Jero
7 min readMay 26, 2020

In this post, I will write about my personal notes and some personal comments from the amazing talk of Hernan Wilkinson about seven design principles with OOP. If you want to see the original video in Spanish you can take a look here.

Introduction

How well our model is reflecting the domain of the problem that we are trying to resolve?

The design of our model, it’s one of the most important parts of our application. How can we measure or know if our model is good or bad? Well, from the point of view of the design, we could say that our solution is reflecting all use cases of the business domain problems that we are trying to resolve. Let’s take a look at the following bad design:

As you can see, on the right side is the real problem that our client exposes to us (eg: Billing system). And the left side is our domain. Each Group contains green circles and red circles, the green circles represent the use cases of the Real domain problem that are on both sides, and the red circles represent the missing use cases that our design missed. From the point of view of design, our Software representation is not covered all use cases, so for that, we could say that it’s not a good design. A concrete example of that could be if we create a Billing system in which we generate invoices that are possible to change after their creation. It is something that does not reflect user needs. Another is the Date object like Calendar of Java.

Next will see a good design in which all use cases of the user domain problem they are covered by our software representation.

A concrete example of that could be the same Billing system that allows us to create an invoice, but now it doesn’t allow us to change them.

Also, Hernan mentioned in his talk, two really interesting tips to help us to don’t miss some use cases (or green circles) of the business domain that we are trying to represent:

  • Imagine that we don’t have computers (like 90 years ago), it’s easier to imagine the domain of the problem in that way because we start to represent the roles of persons, not just things.
  • The use of metaphor it’s so important to start imagining the invisible part of the business domain as a visible one. In that way, we favor interacting with live objects not just a set of instructions. An example of an invisible mode could be Sales. How does a sale smell? What is the color of a sale? It’s complicated to imagine that, so for that, using a metaphor helps us to imagine that.

Also, It was really fun how Hernan, describe the difference between self and this . which basically talks about the perception to interact with non-living things or objects, you don’t say yourself when you feel hungry this feel hungry :D).

Well, let's start with Hernan’s seven principles:

Encourage represent our domain with immutable objects

As we discussed before, each invoice, once we create it, should no longer be changed. So for that, we should think carefully about which data should receive the object invoice at the moment of their creation. So for that, we start to think about the moment to design our object (the representation of the domain problem) first as an immutable object.

But the most important part of working with an Immutable object it’s you don’t longer care about Time constraints. The time axis of our domain problem, it doesn’t longer be a problem for us. In that way, avoid problems like concurrency, and shared states, avoiding side-effects and restricting them in an immutable object, it gives us a benefit to building easy-to-deal-with objects, easy-to-implement time-traveling, referential transparency, able to easily implement pure functions and a lot of properties which come from mostly from functional programming.

Always create complete objects

This principle or heuristic is deeply related to the first one. You should fill each object at the beginning with all data needed to become a valid object. For example, imagine that you are creating a Date object, you should care to pass all fields related to date like: “day, month, year, time-zone” in their constructor object. On the opposite, If you allow creating the Date without all their fields(for instance without timezone), and just set it after their creation with setters, you are violating this principle.

Each object should be valid from their initialization

Each object should be valid after its birth (after the moment that was initialized). A tool that helps us to know that all variants or business rules are satisfied is the exception mechanism. An exception allows us to know when some contract or invariant is not satisfied. In contrast, the documentation doesn’t force you to implement their needs, but the exception it does. Even more, sometimes we do not even know that the documentation exists.

Avoid using setters

This principle is related to Immutability, and also with preserve valid objects along time. Hernan talks about only should be able to implement setters to properties that don’t contain business rules or these properties that don’t affect the validity of the objects themselves. An example of that it’s allowing to change of the description of the invoice. It’s just a comment. Mostly the setters are related to data structures, not living objects.

Only allow atomic changes

In case we need to change the object after its creation, we should be careful about the change containing all the information needed to keep valid the object itself. For that, exceptions will help us again to keep valid our objects. A concrete example of that is our previous example of a Date object containing a method that allows receiving a message syncWith. This method will update all Data content with the Message Data received.

Never use null, undefined, nil, etc

So at this point, we already have complete objects, valid objects, or objects that never were able to change, or if they change, they were changed in an atomic way, I’m wondering if it makes sense to have a Null reference? The answer is No, never.

So, this heuristic is to never use Null. You should not have any parameters, properties, or return reference to Null, nil, undefined, etc

The null is not a polymorphic object, so each time that you ask for some value that may become null, I will need to introduce if a conditional. At this point, problems start to arise. Our code starts to become fragile or error-prone. At this moment you enter into the billion-dollar mistake problem.

To help us to avoid null, we could do the following:

  • Implement Null-Object Pattern in our code, in that way for instance if we are working with user addresses, and the user doesn't add any address, we could represent that state of our domain with an object calledEmptyAddress (each field with default values, like numbers with 0 or emptystrings). Of course, only if, our domain problem allows us to represent in that way, otherwise, we should throw an exception.
  • Another way is to pass a closure that will be executed only if the condition was related to null. In that way, the ifexistence is encapsulated, it’s the same conclusion that the video that I give you before. The point is, sometimes is really difficult to eliminate the if. But the point is, null should be treated as a disease. Should be controlled. Another benefit to be encapsulated it’s when you are refactoring will be easier than it was to spread the conditions along with our system. An example in JS of the encapsulate the if, our user object will return the address , in case that doesn’t exist, it will execute given closure to that message:
const user = {
name: undefined,
getAddress: callback => {
return name === undefined ? callback() : this.name;
}
}
user.getAddress(() => {
// Execute this closure in case that address doesn't exist.
})

Use Metaphors to design our domain problem

Again, it’s so important that metaphors help us to design our software. It enables us to turn visible things that are invisible to their nature. It helps us to have a lens to see our domain problem from a different perspective. The metaphors don't imply should be precisely the same, but it helps us to understand the domain problem and represent it.

Please give me your thought about this short article, I love to share ideas, learn from others and I hope this article may be helpful for someone out there!

Also, you can be following me on Twitter, or contact me on Linkedin.

--

--