In this blog I will explain the 4 pillars of OOP and provide small examples. Small enough examples that you and I can comprehend easily.
Abstraction
Abstraction is essentially "need to know". We hide the major implementation details, while keeping the obvious usage at the forefront. So remember this "hide the details, show the relevance".
Abstraction example...
This is a signup form of an app I'm working on (the relevance).
This is the form's submission implementation logic. (the need to know).
Polymorphism
Polymorphism is when you use one block of code, you change the version of the code being used based on the inputs being giving to it. So to make it a bit clearer, different classes can be used with the same interface but can provide its own implementation of that interface.
Polymorphism example...
class Animal {
speak = () => console.log('makes sound')
}
class Dog extends Animal {
speak = () => console.log('woof')
}
class Cat extends Animal {
speak = () => console.log('meowww')
}
class Cow extends Animal {
speak = () => console.log('moooooo')
}
let dog1 = new Dog()
let cat1 = new Cat()
let cow1 = new Cow()
dog1.speak() // => woof
cat1.speak() // => meowww
cow1.speak() // => moooooo
The Cat, Dog, and Cow classes are inheriting from the Animal class. This allows the Cat, Dog, and Cow class to use the Animal's interface. They only have the speak method available to them though. If we were to leave the speak method out of the Cat, Dog, and Cow class, and then create instances of a Cat, Dog, and Cow class we would still be able to call the speak method on those instances. The only issue is that it would print out the Animals 'makes sound' instead of the appropriate sounds that a Cat, Dog, and Cow makes ('meow', 'woof', 'moo').
This is where method overriding comes in. If we redefine the speak method in the Cat, Dog, and Cow classes, we can customize it to print out the sounds that cats and dogs make.
Encapsulation
Encapsulation binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. A good example of encapsulation is a class. We can actually refer back to the example above where we talk about Dogs, Cats, and Cows with some slight modifications.
class Dog {
constructor(name) {
this.name = name
}
printName = () => console.log(this.name)
}
class Cat {
constructor(name) {
this.name = name
}
printName = () => console.log(this.name)
}
class Cow {
constructor(name) {
this.name = name
}
printName = () => console.log(this.name)
}
let dog1 = new Dog()
dog1.name = "Max"
dog1.printName() // => Max
let cat1 = new Cat()
cat1.name = "Mark"
cat1.printName() // => Mark
let cow1 = new Cow()
cow1.name = "Tom"
cow1.printName() // => Tom
Notice when we create instances for each animal we also assign a name to each. The takeaway here is that the '.name' after each created instances (i.e dog1.name) are all different. Those '.name' are encapsulated within their according classes. Assigning a name to an instance does not modify any other instances name value.
Inheritance
Inheritance is probably the most easiest one to grasp. Inheritance is the concept of one class using(inheriting) the interface of another class. It then becomes a child or subclass while the class that it's inheriting from is the parent or superclass. We actually did some inheriting in our second example above. The Cat, Dog, and Cow class inherits from the Animal class in order to have access to the speak method. Just make sure to add the extends keyword.
Thanks for reading, please do let me know if this was clear or not. Leave a comment below.
Top comments (12)
Nice article. I'd add my 2 cents: While those features are often encountered in OO languages, they are not mandatory. For instance, GO is an OO language in some way, but it does not have inheritance.
And these pillars are not an uniquely OO feature. You'll also find them in FP (Functional Programming) languages. Except maybe for inheritance, which you will of course not find in purely FP languages (Haskell, Elm, ...) - but will find it in most of them as a result of their integration in an ecosystem (Scala, F#, ...).
Richard Feldman speaks about this in one of my favorite tech talks (specifically at 19:55, but the whole talk is really worth watching)
... That said, it does not take anything away from your article :) I just wanted to share this talk.
Can't resist to share this post from the stackoverflow blog:
If everyone hates it, why is OOP still so widely spread?
First of all thanks for a good post!
But in your encapsulation example, I see this :
It works but I suggest you either initialize them from the corresponding classes or change the variable names (dog2, dog3) to make the code clean.
Sorry, my mistake.
Your polymorphism example breaks the Liskov substitution principle. Just use composition and don't make
cow1 instanceof Dog === true
🤦♂️ Did you study biology basics? 😉Principle are "stuff that helps more often than not", not pervasive absolutes on the same level as God's word. But more to the point, it would be weird to talk about inheritance as a "pillar" of oop when general practices have been recommending composition over inheritance for years.
My bad, changed it to inherit from the Animal class. You happy now professor?
You're welcome. Just didn't want to write that the example missed the point completely, that's all. 🤷♂️ But you're definitely on a good path, keep going!
this is definition of object (from OOP)
Encapsulation
as well
Nice explanation, although as far as pillars are concerned, the main one in oop is the concept of objects. Hence the name.
Although as an interesting note, some of these pillars have been noted as broken for a while now (hence the slow ride of fp)
Thanks for sharing
Nice post
Some comments have been hidden by the post's author - find out more