Encapsulation, Polymorphism and Inheritance - The Tricky Trio

Encapsulation, Polymorphism and Inheritance - The Tricky Trio

TL;DR:

  • Encapsulation - Restricting what properties can be called, viewed and set by an object
  • Polymorphism - Changing an object based on the parameters given to it
  • Inheritance - Accessing properties from a class' parent to avoid rewriting code

Introduction 🚀

Even if you're a beginner in Object-Oriented Programming (OOP), you, at some point, will have heard of one at least of those terms; but what are they? That's the question I'm hoping to answer for you in this post. This blog post will assume you have at least a basic understanding of what a class is in OOP.

Encapsulation 🌊

Encapsulation essentially just means "restricting what methods or variables can be changed or viewed by an object". An example of this can be shown below:

public class User {
    private String username;
    private String email;
    private int age;

    // Getter for username
    public String getUsername() {
        return username;
    }

    // Setter for username
    public setUsername(String newUsername) {
        username = newUsername;
    }

    // Getter for email
    public String getEmail() {
        return email;
    }

    // Getter for age
    public int getAge() {
        return age;
    }
}

In this class, we can see that all the variables are private (meaning that objects of the class can't access the variables). We do this because we don't want objects to be able to change any of the variables randomly, we want to restrict what variables objects can view and edit - this is where encapsulation comes in.

Getters and Setters

You'll notice that there's a series of methods that just return a specific variable; they're known as "getters" - they allow instances of the class to view the variable, so if they ever need to view, let's say, the username variable, they can!

But let's say you want to allow User objects to edit the user's username, that's where "Setters" come in. "Setters" are methods that take in a parameter and change one of the variables to that parameter (see the setUsername method for an example). Getters and setters are useful as it allows the user to restrict what variables objects can view and change; if you don't want an object to be able to view or change a specific variable, just don't make a getter or setter for it.

When using getters and setters, remember to set the variables to private and the getters/setters to public. This will make it impossible to do:

object.variableName = ...

You'll need to use a setter instead.

Encapsulation Isn't Just For Variables

You can also use encapsulation with methods (functions inside of classes). If you have methods that you don't need to call from outside of the class (e.g, objectName.methodName()), then you can (and should) set them to private or protected (more about protected in the inheritance section).

Polymorphism 🕊

The second member of this triad is what's known as "polymorphism". Put shortly, polymorphism is "changing an object based on the parameters given to it". Take the below class as an example:

public class Car {
    private String manufacturer;
    private String colour;
    private int numOfDoors;

    public Car(String carManufacturer, String carColour) {
        manufacturer = carManufacturer;
        colour = carColour;
        numOfDoors = 4;
    }
}

In this class, when the user is creating an instance of the class, they can pass in the car manufacturer and colour of the car into the constructor, and then it will set the parameters accordingly. The only problem is that in the constructor, numOfDoors is set to 4; but what if the car has a different number of doors? That's when "Constructor Overloading" comes into play... but what is it?

Constructor Overloading

If you're not familiar, a constructor is a type of method that is called when an object is created. It's used to set the necessary variables and to call the necessary methods to initialise the object. It always has the same name as the Class it's in (including capitals).

Constructor overloading is just having more than one constructor in your class, but each constructor takes in different amounts of parameters - the amount of parameters you use to create an object determines which constructor is called.

Going back to Car example we're using, let's add some constructor overloading by adding a constructor that also allows the user to set the number of doors:

public class Car {
    private String manufacturer;
    private String colour;
    private int numOfDoors;

    public Car(String carManufacturer, String carColour) {
        manufacturer = carManufacturer;
        colour = carColour;
        numOfDoors = 4;
    }

    public Car(String carManufacturer, String carColour, int carNumOfDoors) {
        manufacturer = carManufacturer;
        colour = carColour;
        numOfDoors = carNumOfDoors;
    }
}

Now, you're able to set the number of doors if the car doesn't have 4.

Inheritance 👩➡👦

Inheritance is all about getting properties (variables and methods) from a parent class. In Object-Oriented Programming, you may often find yourself making classes that are similar to one-another. This is where inheritance can be useful.

Inheritance is all about not rewriting code.

Let's say you're making a program with multiple classes, all of which are a type of land vehicle (e.g, car, truck, bus, motorbike, etc). You may want to add similar methods for each of these classes (such as: drive, stop, engine on/off, etc), but this wouldn't be the best practice - you'd be repeating yourself, instead you should use inheritance.

Instead of copying and pasting the methods across classes, you can make a "parent/super class" and make the methods in that class, then have each of the land vehicle classes inherit those methods (meaning they can run the methods just as if you put the method in that class originally).

Here's an example, let's say you have two classes, a car and motorbike:

public class Car {
    public void drive() {
        // ...
    }

    public void stop() {
        // ...
    }
}
public class Motorbike {
    public void drive() {
        // ...
    }

    public void stop() {
        // ...
    }
}

You can see that both of the classes have the same methods, so to keep our code DRY (Don't Repeat Yourself), we can make a parent class and put the drive and stop methods in there:

public class Vehicle {
    protected void drive() {
        // ...
    }

    protected void stop() {
        // ...
    }
}

and change the Car and Motorbike classes to inherit the methods from the Vehicle class:

public class Car extends Vehicle {}
public class Motorbike extends Vehicle {}

*the "extends" keyword is how you turn a class into a subclass in Java, it'll take any method or variable labeled "protected" in the Vehicle class and pass them into the subclass (the Car/Motorbike). You'll need to check how to make a subclass in the language you're using.

Instances of the Car and Motorbike classes are now able to call the inherited methods as if you hard-coded them into the Car and Motorbike classes themselves (as shown below).

Car car = new Car();
car.drive();

Motorbike motorbike = new Motorbike();
motorbike.stop();

Conclusion

Encapsulation, polymorphism and inheritance are fundamental features of Object-Oriented Programming and understanding them is key for writing good code. I hope that this post has helped you understand them and if not, then please feel free to leave a comment asking any questions you may have!

Constructive criticism is welcome, this is my first blog post, I don't exactly have it all figured out. Thanks for any and all help!