Thinking Object

Step one in the transformation of a successful procedural developer into a successful object developer is a lobotomy

David West, Object Thinking

It takes a lot of time and effort to think object and to think away from procedural thoughts (jot that down).

Turns out, us human can very intuitively conceive objects, initially. But us also programmers, so "us" turned away from our human thinking and embraced the computer thinking.

What's in a program ?

For many of us, a program is merely a suite of procedures dealing with data. It's data-in, data-out. We see programs through the lens of the structured programming paradigms. Or, to quote Niklaus Wirth, as the sum of Data Structures & Algorithms.

I mean, it's technically true. Compile your classes & objects and you will see (mangled) functions and data structures. There's no place for objects in the world of bits.

Well, if that's how the computer work... is it really a bad thing to conceive programs that way ?

Sort of.

But first, what kind of programs are you working on ?

Are you working on small shell scripts, maths programs, data transformation pipelines and embedded systems ?

Or are you working on products & web applications, text editors and GUIs ?

When you're working, are you thinking in terms of data storage, transformations, memory allocation and threading ?

Or are you thinking in term of users registering, changing their settings, publishing posts and analyzing analytics ?

See, most of us tend to think in terms of high-level concepts that belong to our language and not the language of the computer. We use these words to exchange about the software with Muggles. After all, we create software for them to use, and when they do use it, they describe what they're doing with their own vocabulary.

Yet, taking user requirements and properly transforming them into code is probably one of humanity's hardest problem. We've been doing so for almost 70 years and still relentlessly fail. We are trenched in the battlefield fighting for our life against our biggest enemy : bugs.

Some of these bugs can be credited to how complicated it is to develop some intrinsically complex tasks. Parallel programming is an example, GUI is another. It takes time and effort to become more competent in these technical areas, but most has already been written about these subjects. In other words, we know how to write good technical programs, but some people are more skilled than others. It's only a matter of education.

But I suspect more of these bugs are not of technical nature, or at least partially. From my experience, most of the problems arise from the design of the code itself and how it poorly translates to what the system is supposed to be doing.

There's a huge discrepancy between the code written and what it's supposed to do, and so it misbehaves. The more we add code to it, the more it misbehaves. The code gets patched to compensate for its lack of design, and nobody dares to touch the mess.

Because writing code is hard.

We are asked to write code for today, and to have it flexible enough to change tomorrow. Yet, we can't know in which ways that code will change, and odds are today's code is a poor reflection of what's to come. Heck, in most cases, we don't even know what's to come.

And code breaks, and it's hard to get it fixed, because it's entangled with a lot of code dealing with something else.

I could expand on and on, but you get the idea : complex code is the source of all evil, and we must strive to push that complexity asymptotically to 0.

How are objects helpful in this effort ?

They bridge the gap.

Brave New World

It's in the nature of humans to deal constantly with objects.

A cellphone is such an object. We can make calls, browse internet, buy stuff. It's a conceptual whole. It has everything it needs to perform these tasks, we only have to use its public interface to do so.

A car is also an object. We can start the engine, brake, open the trunk and so on. It's also a conceptual whole. That's how we manipulate the car.

It's really natural for us to think of it this way. Wouldn't it be nice if we could, word by word, write this down in our code and have it work ?

Want to send an e-mail ?

const email = new Email()
                .writeSubject("Hello Everyone")
                .writeContent("...")
                .attachFile(new PDF("..."))
                .target(new Target("johndoe@gmail.com"))
                .send(mailer)

How does the Email and Mailer object work ? At this point, we don't know and we don't care. What matters is the language we use to manipulate these objects. That language is close to the way we talk and think about e-mails, it'd be very hard to mess this code up.

Want to schedule a driving lesson with an instructor ? Be my guest.

const student = Students.find("student-id");
const instructor = Instructors.find("instructor-id");
const school = School.get();
const garage = Garage.get();

Invariants.check(student.isAvailable(date), "the student is not available");
Invariants.check(student.hasEnoughCredits(), "your balance of credit is insufficient");
Invariants.check(instructor.isAvailable(date), "the instructor is not available");
Invariants.check(school.isOpen(date), "the school is not opened");
Invariants.check(garage.isCarAvailable(date), "no care is available")

const car = garage.allocate(date);
const duration = new Duration(durationInHours);
const creditsToPay = new CreditsToPay(date, duration);

const lesson = Lesson.reserve(student, instructor, car, date);

student.consumeCredits(creditsToPay);
student.addToSchedule(lesson);
instructor.addToSchedule(lesson);
car.addToSchedule(lesson);

Lessons.save(lesson);
Students.save(student);
Instructor.save(instructor);

Does it looks complicated ? How close are we to the human, and how far from the machine ?

This code exists, I wrote it, and it works in production. It's not a fantasy. It's real. We had a conversation with a business owner and he really said things along the line of :

First, we must ensure both the student and the instructor are available at the same moment, we can't have overlapping courses. The software should help us prevent the scenario where two students come in at the same time, booked on the same instructor. That'd be very unpleasant.

Next, the student will buy credit points, and each hour of lesson will cost him credit points. Hence, when the student reserves a lesson, he must have enough points to reserve the lesson. They are then consumed goods.

Also, a car must be allocated to the lesson. We have only a limited amount of cars, so we must avoid having the same car reserved twice at the same time.

Does the code looks like a good translation of these requirements ? It's hella easy to check, it's less than 20 lines.

This code is somewhat procedural. That's necessary because the requirement itself is procedural. I'd be hard to turn these requirement into objects. Honestly, I have no idea how we could.

But this procedure deals with many small objects, themselves composed of objects. At this 100 feet view, we have all we need to deal with the big picture. If we want to dive deeper and get into the details, we can, but we don't need to right now.

If a new requirement comes in, we can easily add it into our 100 feet view and deal with the how later. But before the how comes the what.

That's how I conceptualize my programs, by the way. I roughly sketch the big pictures and create pseudo-code by wishful thinking. It helps me imagine what the ideal objects would be, how they would behave. At this point I can easily confirm the behavior with my customer, and later move onto the details which are more technical.

Here's the magic. I can confirm the behavior with non-technical people, because my code speaks their language.

I can create the objects they tell me about and later figure out how to fit it into the code. And I can put the bulge of the behavior into these objects.

Each object carries a little bit of the complexity, and that complexity is spread out so that I can tackle them one at a time.

In the end, I can connect all the wires, all the objects together, and watch my creation take life.

And scream : "it's alive !"

My, how gracious and how simple all of this is !

The customer can come in anytime with new requirements, such as

  • The student can't book more than 2 sessions per week

  • Instructors can sometimes take vacation days and become unavailable

  • Finally the student pays 2 credits per hour instead of 1

  • We have a partnership with SuperCar and need to connect to their API for the car allocation algorithm

Whatever comes in, the code is ready to adapt without changing any of the other bits.

And this, my friend, is the power of abstraction & encapsulation.

  • Abstraction : in this procedure, we deal with a very high level view of the code, descriptive enough to let us think in terms of high concepts, easily verifiable and assertable.

  • Encapsulation : the details are wrapped in the objects, and we never ask them for private informations. We only deal in terms of behavior.

Defining Object-Orientation

As object thinkers, we really want to see our program composed of objects whose root are found in our natural language. And as such, we consider the object being the atom of our programming universe, our fundamental unit of understanding.

Everything is an Object

Hence, the very first axiom of this paradigm is the following : everything is an object, and nothing exists outside of objects.

Objects are a transposition of reality in our mind, and then in our code. It's very common to anthropomorphize them (read, treat them like persons) because the metaphor really helps grasping the intuition behind objects. We want to work with objects collaboratively and with their permission, not controlling them like puppets.

Thus, an Object is very independent in nature, and it exists to carry a behavior.

To anthropomorphize again, an Object exists to serve. If it has no behavior, it's a dead weight for the whole society. We need to feed them and take care of them because they're unable to do so themselves. In an Object world, such object is harmful for the whole community of objects.

The metaphor only goes so far and isn't a proper depiction of a human society. Object world is harsh, cold and merciless. Human society, less so.. I hope.

Hence, every Object has access to everything it needs to perform its duty. In a proper Object society, every Object is given the tools it needs for the task. Such tools are called Collaborator Objects and, from the point of view of our Object, are simply assistants.

So, Objects are not supposed to know everything in detail.

A baker will use an oven, but the baker doesn't need an Electrical Engineering Ph.D to use that oven. As far as the baker is concerned, it's a matter of pushing buttons and putting the bread inside.

The oven proposes baking Services to the baker and keeps the internal of how it does so private. These internals are only relevant for the manufacturer and the technician responsible for repairing the oven, aka the Implementor.

Encapsulation

Objects dream of independence. In many projects, they wake up every morning wishing they we'd set them wild and free. But they're kept prisoners of the procedural mode of thinking.

Hence, the object's most important & essential value is independence.

How do we achieve independence in our programming world ? By the mean of reducing coupling.

Coupling determines how tightly connected two components are (objects, modules, systems). The more coupled they are, the more they depend on one another, and the less flexible they are.

See coupling as planning a trip with a group of friend. Every time a detail of the trip change, every friend must be noticed and adapt. Change the departure from one day to another at the last moment and everybody panic.

The more you know the details of something, the more coupled you are.

Hence, more coupling leads to less flexibility and more rigidity.

But objects won't go far without communicating with other objects, right ? They work as a team, and the more complex the task, the more they need to collaborate. Alas, we can't completely get rid of the coupling or our objects won't be useful at all, but we need to control that coupling.

How do we do that ? By adhering to a public contract and sticking to it.

The baker's promise is to bake you some fresh bread. They (metaphorically) signed a paper stating he will serve bread until the end of their life. What a dedicated baker they make !

Well, they don't have a choice. Would the baker break the contract, chaos would follow. Because that's his public contract, and many other people are relying on this contract do to their work.

By now you've probably come to understand the contract we're referring to is the interface of the object.

That's the public part. However, the baker is given total liberty to how they make bread. They are free to change and adapt their method, hone their craft, make experiments, as long as they maintain their part of the bargain.

And that's crucial to the baker. They want to keep that liberty all for themselves. They don't want people to rely on a particular way of cooking bread, or on them using a specific brand of oven. It's the baker's territory, and entering it means war.

That liberty is called encapsulation.

And it's an object's most cherished treasure, their most important gift. And they'll stop at nothing to keep that treasure in their chest.

Because that's the key to their power. This total liberty allows them to change at will, evolve and adapt to new needs without ever breaking their contract with other objects. Should they break their contract, the impact would be limited to their carefully crafted public interface.

That's why the public contract always, ever come first in object-design. It's the behavior of the object, what makes it alive. It's what other objects see, and how they collaborate. It's the alpha and the omega of our object world.

And that's the biggest difference between data-driven design and behavior-driven design.

In data-driven design, we first view the world as data and decompose of our world according to that data.

In behavior-driven design, we view the world as actors performing actions and decompose our world according to the behavior.

It's a central difference, because data-driven design rarely leads to object thinking and too often makes us treat objects as mere data-structures.

On the opposite, behavior-driven design leaves us no choice.

Choreography

In a Choreography, each dancer knows what they need to do and communicate with each other with no mediator in between. They're independent and trustworthy.

In an Orchestration, the maestro manages the direction of the piece and tells each performer what to do.

Choreography and Orchestration are very useful metaphors in a microservices world. Turns out, microservices are a perfect depiction of what objects should be.

Independent units of behavior, evolving and changing on their own, communicating asynchronously and performing their duty without anyone telling them how to do so.

If that's not an object, I don't know what is !

Hence, Orchestration means having a puppet master managing each service to perform a big task. That master would tell the first object to do something, and another object to do something after that, and so on. It knows the big picture, the objects don't. They're totally unaware of the context they're being used for. They're too dumb to understand anyway.

It has some benefits. Orchestration allows the master object to make the big picture explicit. Everything is just there, we know where he's going.

But that's not very respectful, right ? We tell objects they're not trustworthy enough to carry such a big task on their frail shoulders. Rest assured, objects, the master is watching.

No, that's definitely not a good way to treat objects.

We want them independent, give them enough informations to know the details of the operation they're carrying, and reward them for being part of something bigger than them.

As our deeply beloved master said.

The whole is greater than the sum of its parts.

Aristotle.

Hence, splitting complexity among objects and letting them do their job is key to understanding objects.

Every object performs its duty and notifies other objects on their progress. Each other object reacts accordingly, does their part of the job, notifies other objects and so on. Once the first object is set to motion, the whole process starts.

And us, humans, can stand back and watch the Goldberg machine in action.

Messaging

Objects perform their duty in choreography through the means of notifications, a special form of messaging. Hence, a messaging mechanism must be in place for objects to communicate. Often, they do so using an Observer pattern.

But messages are far more important than just notifications. Messages are a way to have an object do something !

There's typically 3 types of messages :

  • Notifications : we just saw them, they're a mean of telling an object that something happened

  • Commands : the most prevalent form of behavior. We ask an object to do something for us.

  • Queries : we ask an object to gives us some information

The astute reader may see a parallel to Command/Query Separation and would be right to do so.

If you've been programming in any popular OO language like Java, C# or JS, you'd send a message to an object through the mean of function invokation.

But if you've been programming in Objective-C or in Smalltalk, you'd send a message to an object through the mean of... a message, literally. And that difference is very important for OO thinkers.

In the end, sending a message to an object boils down to executing a function. So what's that "sending a message" non sense OO aficionados are talking about ?

Technically, the code generated by these two semantics is different. On the first case, we can sometimes resolve the function be called statically and it really ends up being a function call.

On the second case, the mechanism is heavier and more indirect, because even in the case of the compiler knowing which function to call, the generated code will actually perform a runtime lookup. That process is called Late Binding.

The dot notation is an optimization by modern languages to make OO code more efficient, because it tries to avoid late binding whenever possible. After all, late binding means more work at runtime, means slower code. But at the benefit of more flexibility.

Because you see, Late Binding means an object can evolve, and its code can change while the program is still running. After all, the program has no hard links to the object's methods, it always performs a lookup before invoking behavior.

That's analogous to how microservices can be deployed independently without breaking other services. They can be put down, upgraded, put back up without the whole system collapsing. Which is not possible is a monolithic architecture.

Actually, Microservices really are the pinacle of Object Thinking !

Why is that important ? Because as stated numerously, objects want to be independent. They don't want to break and stop working because another object needs maintenance. Any other object can say "I need a pause" and take it without breaking the whole system. That's key to a resilient system.

The Vision

Almost 50 years ago, Alan Kay, considered the father of object-orientation, had a vision.

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

Alan Kay

That definition cover everything we've treated so far, and all the text you've read leads up to that definition.

  • Messaging : we've seen how messaging is key to keeping objects independent without any overlord directing their work

  • Local Retention, Protection & Hiding of State-Process : that's encapsulation. Objects keep their internal for themselves, and expose only what's necessary for other objects to perform their duty, no more no less.

  • Extreme Late-Binding of all things : communicating with an object means sending a message to it, not invoking a function. Even though in the end, a function will be invoked, the mechanism (how it's done) and the semantic (what it means) is completely different.

If you can understand this, then you're among the very limited group of people with a firm grasp of object orientation.

Summary

You know have a very good idea of what's inside Object Thinking. Here's a summary.

  • Object : the fundamental unit of understanding. Everything is an object. It performs behavior and collaborates independently with other objects without being controlled. How that behavior is carried is none of the outside world's business. We trust them wholeheartedly and let them do what they do best.

  • Collaborator : from the point of view of an object, a collaborator is another object providing helpful services to carry its own duty

  • Implementor : an object's code is implemented by a developer. They create both the public interface they swear to maintain, and the internal code they're free to develop.

  • Service : aka public interface. An object exists to serve someone, quite often other objects. They do so by offering one or many services, and they swear to carry their duty diligently.

  • Encapsulation : protecting an object's internal structure, how it does its work.

  • Messaging : the mechanism through which objects communicate, in the form of Notifications, Commands & Queries.

  • Late-Binding : the mechanism of sending a message to an object at runtime rather than knowing at compile time which method to invoke (rough explanation, the technical details are actually more complex, but the topic at hand is complex enough to abstract away the details).

Last updated