One day I realized that I didn't understand what "true OOP" is, so I began digging, and during my research I was amazed by discoveries that I had practically never heard from any author.
And to help you follow my path and understand how I came to a fairly unique but comprehensive definition of OOP, let's list what we definitely know about OOP:
- There's a definition from the creator of OOP, Alan Kay: "OOP is 'objects' hiding (encapsulating) data and behavior and communicating through messages"If visualized, we get a graph where the edges are messages and the nodes are "objects":
And yes, Alan Kay, being a molecular biologist by education, was inspired by how cells in organisms interact.
- There are 4 pillars of OOP:
- Object Encapsulation – not to be confused with just the term "encapsulation," which means "hiding" and is used in various areas of computer science. "Object encapsulation" involves hiding properties and methods of an "object" entity in code
- Inheritance – this is self-explanatory
- Object Polymorphism – same with polymorphism, because it's a general concept in computer science and there are different variants of it (such as interface polymorphism)
- Abstraction – I won't comment on this one either
- There are OOP patterns and principles: Singleton, Factory, Builder, Prototype, Decorator, Facade, SOLID, GRASP, Rich Domain Model, etc.
- And there is OOP syntax with operators like
class
,inherit
,implements
, etc.
Having gathered these "clues," I asked myself the following questions:
- Does Alan Kay's definition apply only to codebase?
- Can I write an OOP program WITHOUT using patterns and principles?
- Can I write an OOP program WITHOUT resorting to any of the 4 pillars?
- And of course, can I write a program that follows Alan Kay's principle, pillars, principles, and patterns WITHOUT using OOP syntax?
Let's figure this out.
1. Does Alan Kay's definition apply only to codebase?
Let's take the definition from OOP creator Alan Kay:
"OOP is 'objects' hiding (encapsulating) data and behavior and communicating through messages"
Think for a moment, isn't something like a "microservice" the very "object" from his definition?
- Microservices are self-sufficient and encapsulate all the logic
- Microservices communicate with each other through serializable messages (API)
So when visualized, we get the same OOP:
If we go one level lower, we'll find that Processes handled at the CPU kernel level are also "objects" that don't share memory with each other (they encapsulate) and can communicate using external transport and serializable messages.
It turns out that absolutely any application or parts of a system can be viewed as examples of "OOP" implementation. And this is a given simply because this is how hardware and operating systems are designed.
But there are also approaches that use "OOP" and can be added by our personal choice, for example, Actor Model.
And it's also surprising and quite funny that practically the only language that uses "OOP" to the maximum is Erlang, one of the most canonical functional programming languages...
The essence of Erlang is that each separate function is a "mini-application" that communicates with other functions through a message queue.
Again, when visualized, we get "OOP":
So the answer to the question is: Alan Kay's definition of "OOP" is both an integral part of any system and an opportunity to build your own patterns.
2. Can I write an OOP program without using patterns and principles?
SOLID, DRY, KISS, Singleton, Abstract Factory, and so on - these are all just tools that we may or may not use to create our program.
Yes, most likely, while you're building it and trying to maintain OOP principles, you'll end up reinventing these patterns and principles because you'll face the same problems as their authors.
But at the same time, no one obliges you to follow them, and you can write a program without applying them at all.
Answer: yes, you can write an OOP program without using patterns and principles.
3. Can I write an OOP program without resorting to any of the 4 pillars?
Namely, Object Encapsulation, Inheritance, Object Polymorphism, and Abstraction.
Abstraction I'll skip because while the other pillars can be expressed as programming language mechanisms, OOP Abstraction is more of a principle, which as we found out can be unused.
Inheritance has long been criticized, and many people lean towards using Composition (link), so this pillar is also optional.
Object Polymorphism stems from Inheritance, so it's also often replaced with Interface polymorphism (link to one of the best articles on the topic), making it optional as well.
But you can't get rid of Object Encapsulation in OOP. Because as soon as we remove encapsulation, we violate absolutely all principles of OOP, from any perspective.
Because encapsulation is what gives the main distinction between procedural code and OOP code.
Even if you violated it in just one place, you instantly broke the principle of dividing the program into "objects that encapsulate data and behavior, communicating with messages," because data starts to leak out, and along with it, the behavior over this data begins to extend beyond the specific object.
Answer: we can omit Abstraction, Inheritance, and Class Polymorphism, but Object Encapsulation is the core principle of OOP.
4. Can I write a program that follows Alan Kay's principle, pillars, principles, and patterns WITHOUT using OOP syntax?
Above, we found that as long as we adhere to "Object Encapsulation," our program adheres to OOP.
Accordingly, here's an example using OOP syntax:
class HashedPassword { public password: string; constructor( password: string; ) { this.password = hash(password) } } class User { constructor( private HashedPassword: HashedPassword | null; private activated: boolean; ) {} setPassword(password: string) { user.password = new HashedPassword(password); user.activated = true; } }
And the same code, but without OOP syntax:
type HashedPassword = ReturnType<typeof HashedPassword> function HashedPassword (password: string) { const state = { password: hash(password) } return { state } } password: string; constructor( password: string; ) { this.password = hash(password) } } type User = ReturnType<typeof User> function User ( password: HashedPassword | null, activated: boolean; ) { const state = { password, activated, } return { setPassword: (password: string) => { state.password = HashedPassword(password); state.activated = true; } } }
And it turns out that from an OOP perspective, this is absolutely identical code, because User and HashedPassword encapsulate data and behavior. The only difference is that we had to add a bit more type notation, which is automatically created when using OOP syntax.
So the answer is: yes, we can write a program following all the pillars, Alan Kay's definition, principles, and patterns of OOP, while NOT using OOP syntax.
Summary
- Does Alan Kay's definition apply only to codebase?
- No.
- Can I write an OOP program without using patterns and principles?
- Yes.
- Can I write an OOP program without resorting to any of the 4 pillars?
- No, Object Encapsulation is mandatory.
- And of course, can I write a program that follows Alan Kay's principle, pillars, principles, and patterns WITHOUT using OOP syntax?
- Yes.
It turns out that OOP is multifaceted, and by highlighting these facets, I came to the conclusion that there are 3 types of OOP.
3 types of OOP
- OOP syntax – syntax given to us by the programming language, with operators such as
class
,inherit
,implements
, etc.
- OOP architecture – dividing the system into separate "objects" that encapsulate logic and behavior and communicate with each other through messages.
- OOP methodology – those same 4 pillars and, as a consequence, all patterns and principles designed to "facilitate" working with OOP methodology.
Actually there are 4, but that's not important
Alan Kay's original definition also includes mechanisms that must be available in the programming language itself to achieve that "perfect OOP," but they are so specific that he had to write his own language (SmallTalk), and no other programming language has used these mechanisms, so we'll omit "OOP as a programming language design" for irrelevance.
OOP syntax
Using OOP syntax is a matter of taste, and the developer can decide whether they want to use a language with OOP syntax.
At the same time, as we'll see in the next chapter, you can write code using OOP syntax but not applying OOP architecture and methodology.
But since this is a matter of taste, let's move straight to the meat.
Constructive OOP architecture
As we saw above, Alan Kay's definition is part of any system (microservices, process design, etc.), but can also be used as an architectural approach (Actor Model, Erlang runtime, etc.)
Therefore, OOP Architecture is either inevitable or an extremely useful approach to certain tasks.
Destructive OOP methodology
But OOP methodology is an infection that has destroyed billions of nerve cells over millions of hours of writing and debugging OOP code.
Why am I so harsh on the topic of OOP? You'll find out in the next chapter:
👈 Previous chapter
Next chapter 👉