This article is an excerpt from my book about Data-Oriented Programming.

More excerpts are available on my blog.


In the previous part, you presented your design to Dave.

In this part, we continue our exploration of the tendency of OO systems to be complex.

What's wrong with you OO?

Before you start coding, and while you prepare yourself another cup of coffee, I would like to challenge your design. It might look beautiful and clear on the paper but, this design is too complex. It’s not that you picked the wrong classes or that you misunderstood the relationships between the classes. It’s much deeper. It’s about the programming paradigm you chose to implement the system. It’s about the Object Oriented paradigm. It’s about OO.

OO is too complex.

The purpose of this book is to present a much simpler programming paradigm called Data Oriented Programming (DO). If you are already convinced that OO is too complex, you can skip to the next chapter and start your journey into DO.

If you are a skeptic about that, please keep reading.

According to DO, the main sources of complexity of your system - and of any OO system - are:

  1. Code and data are mixed

  2. Objects are mutable

  3. Data is locked in objects as members

  4. Code is locked into classes as methods

In this section, we are going to illustrate each of the above points in the context of the library management system and explain in what sense it is a source of complexity.

Code and data are mixed

One way to assess the complexity of a class diagram is to look only at the entities and their relationships (ignoring members and methods) as in A class diagram overview for a Library management system.

Don’t you think this diagram is too complex for such a simple system?

complex class relation
Figure 1. A class diagram overview for a Library management system

When we design a system, no matter whether it’s OO or DO, we have to define the relationships between different pieces of code and data: that’s unavoidable.

In OO, code and data are mixed together in classes, data as members and code as methods. From a system analysis perspective, the fact that code and data are mixed together makes the system complex in the sense that entities tend to be involved in many relationships.

Take a look for instance at the Member class in our class diagram A class diagram overview for a Library management system. It is involved 5 relations: 2 data relations and 3 code relations.

  1. Data relations:

    1. Library has Members

    2. Member has BookLendings

  2. Code relations:

    1. Member extends User

    2. Librarian uses Member

    3. Member uses BookItem

Imagine for a moment that we were able somehow to split the Member class into two separate entitites:

MemberData for the data

Instead of a Member class with 5 relations, we would have as shown in A class diagram where Member is split into code and data entities:

  1. A MemberCode entity with 3 relations

  2. A MemberData entity with 2 relations

member code data
Figure 2. A class diagram where Member is split into code and data entities

Not yet convinced that this imaginary system is simpler than the original one?

No problem, I know how to convince you.

Let’s split every class of our original class diagram into code and data entities. Now we have a system made of two disjoint parts as shown in A class diagram where every class is split into code and data entities:

  1. A part that involves only code entities

  2. A part that involves only data entities

data code relation
Figure 3. A class diagram where every class is split into code and data entities

I hope that you agree that this system, made of two disjoint sub-systems, is simpler than the original one. It’s not simpler by accident, it is a logical consequence of separating code and data.

Important
A system made of disjoint simple parts is less complex than a system made of a single complex part.

Objects are mutable

You are probably a bit tired with all the system-level analysis that we presented in the previous section.

Let’s get refreshed and look at some code. Well not exactly code, but pseudocode.

Please take a look at the pseudocode shown in [code-simple]: we get the blocked status of a member and we display it twice.

If I tell you that when I called displayBlockedStatusTwice, the program displayed true on the first print call, can you tell me what the program displayed on the second print call?

class Member {
      Bool member;

      void displayBlockedStatusTwice() {
         isBlocked = this.isBlocked()
         System.Print(isBlocked);
         System.Print(isBlocked);
    }
}

"Of course, it displayed true again", you tell me.

And you are right.

Now, please take a look at a slightly different pseudocode as shown in Apparently simple pseudo code: here we display twice the blocked status of a member without assigning a variable.

Same question as before: If I tell you that when I called displayBlockedStatusTwice, the program displayed true on the first print call, can you tell me what the program displayed on the second print call?

Apparently simple pseudo code
class Member {
      Bool member;

      void displayBlockedStatusTwice() {
        System.Print(this.isBlocked);
        System.Print(this.isBlocked);
    }
}

If your answer is false, I am suggesting you to consult a therapist.

If your answer is true, I am suggesting you to take a few seconds and think about it again.

I’ll give you a clue: multi threading.

The correct answer is: in a single threaded environment, it displays true while on a multi threaded environment it’s unpredictable.

Indeed in a multi threaded environment, between the two print calls, there could be a context switch and the state of the member object could be changed (e.g. a librarian unblocked the member).

The difference between the two pseudocode snippets is that:

  • in the first snippet, we access twice a boolean value which is a primitive value

  • in the second snippet, we access twice a member of an object

This unpredictable behavior of the second snippet is one of the annoying consequences of the fact that in OO, unlike primitive types who are immutable, object members are mutable.

One way to solve this problem in OO is to protect sensitive code with mutexes, but it introduces issues on its own like a performance hit and a risk of deadlocks.

We will see later in the book that DO treats every piece of data in the same way: both primitive types and collection types have immutable values. This "value treatment for all citizens" brings to DO developers' minds a lot of serenity. As a consequence, more cells of DO developers' minds are available to handle the interesting pieces of the applications they build.

Until then, keep you mind busy with boring stuff.

When you feel ready to explore more sources of complexity in OO, move to the next part.

This article is an excerpt from my book about Data-Oriented Programming.

More excerpts are available on my blog.