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.