Abstraction, Encapsulation & Inheritance
Tags: design, java, beginners, programming
Published: 2022-01-11 01:32:10

Hopefully, these explanations make the principles easier to understand. In this article, I'll tackle Abstraction, Encapsulation and Inheritance and illustrate them with examples written in Java.

Polymorphism deserves an article for itself, so I'll publish that at a later date.

Let's dive in.

## Abstraction

When you're driving a car and you want it to go faster, what do you do? You *"step on the gas"*. In other words, you step on the gas pedal and expect the car to accelerate. 

If you want to go slower, you step on the brake pedal so that the car will *decelerate*.

Most likely, you do this without stopping to think about what's happening behind the scenes. You don't need to know the inner workings of the throttle valve, the engine or the braking system when you want the car to go faster or slower.

This emphasis on the essential details is the beauty of Abstraction. It allows you to focus more on the idea and less on how things get done ie. the implementation.

Once you understand Abstraction, you will start to see it all around you.

### Two benefits of Abstraction

1. It reduces complexity by only presenting the necessary level of detail.
2. Abstraction gives us the flexibility to change the implementation without disturbing the interface (the part that the user/client interacts with). 

One example of the second benefit is Tesla's Over-The-Air (OTA) updates which allow the company to improve how their vehicles operate without significantly changing how their owners interact with the vehicle.

### Two drawbacks of Abstraction

1. Too much Abstraction can make the code bulky.
2. Poor Abstraction can make the code confusing or misleading. 
Imagine flipping a light switch that turns on the light, but water also comes pouring out of your garden hose. This would violate your expectations.

### Abstraction example in Java

Two popular ways of accomplishing Abstraction in Java is through the use of `abstract` classes or `interfaces`.

Let's use an `abstract` Car class to illustrate this principle.


```java
abstract class Car {}
``` 

Without needing to go into the engineering details, we know that we expect a Car to be able to accelerate and decelerate.

Therefore, we can translate these expectations into `abstract` methods.

```java
    abstract class Car {
        protected abstract void accelerate();
        protected abstract void decelerate();
    }
```

Even though we can create different Cars based on this `abstract` class, each type must possess the ability to `accelerate()` and `decelerate()`. 

How each car decides to accelerate and decelerate is its business.

```java
    abstract class Car {
        protected abstract void accelerate();
        protected abstract void decelerate();
    }
    
    public class HondaCRV extends Car {
        public void accelerate();
            //Go faster
        }

        public void decelerate() {
            //Go slower
        }
    }
```

Encapsulation often gets confused with Abstraction because they both seem to be "hiding something". 

Encapsulation is mainly about two things:
1. Keeping data and the code that operates on that data all in one place ("capsule"), and
2. Restricting access to that data.

When you think about it, it makes sense. Why not keep everything that's closely related together? 
It's not much different from keeping all your books on the bookshelf, or all the dishes in the kitchen.

If done properly, then the data is treated like the special plates and glasses in the kitchen that only get brought out when guests come over.
The only way you can access them is by going through the "proper channels", or risk an ass-whooping. 

In the same way, encapsulated classes make their data private and specify the getters and setters which must be used to access the private data.

Instead of simply modifying the private data, setters can perform checks to ensure that the data is being set to a valid value.

Or they can perform operations to ensure that all the data are kept up to date with each change.

If you really want to take it up a notch, the getters and setters are *both* optional. This means that you can make the class read-only or write-only.

On the flip-side, if there was unrestricted access to these data members, then keeping the data valid would be the responsibility of error-prone software developers.

### Two benefits of Encapsulation

1. Better organization and readability of the code.
2. Enhanced security of the data in a class.

### One drawback of Encapsulation

1. The size of the code may increase as getters or setters (or both) may be added to the data members/fields.

### Encapsulation example in Java

Let's continue with the special plates and glasses example to show Encapsulation in action. 

First, we declare the fields and initialize them to an arbitrary quantity:

```java
public class Kitchen {
    private int plates = 10;
    private int glasses = 10;
}
```

We can add an additional field to indicate whether or not permission is granted to remove the plates or glasses from the kitchen.


```java
public class Kitchen {
    private int plates = 10;
    private int glasses = 10;

    private boolean hasPermission = false;

    public void setPermission(boolean isPermitted){
        this.hasPermission = isPermitted;
    }
}
``` 

Okay, that's lovely so far. Now it's time to create a `getter` for removing plates from the Kitchen.


```java
public class Kitchen {
    private int plates = 10;
    private int glasses = 10;

    private boolean hasPermission = false;

    public void setPermission(boolean isPermitted){
        this.hasPermission = isPermitted;
    }

    public void getPlates(int numberOfPlates) {
        if(hasPermission == true) {
            this.plates -= numberOfPlates;
        }
    }
}
``` 

If the `getPlates` method is called, it will first check to see if `hasPermission` is set to `true`. 

If so, then it will subtract the number of plates specified from the total plate tally in the `plates` field.

In the code above, `getPlates()` is the only method with permission to alter the value of `plates`. If you want, you can add a `setter` too.

## Inheritance

When you look at a group of objects, people or places you'll subconsciously detect if they are similar or not.

This is because we understand that similar things share common traits and/or behaviours while dissimilar things don't. 

Most dogs bark, wag their tails and drive you crazy in the wee hours of the morning. Similarly, the average car has a gas pedal, a brake pedal and a steering wheel.

So, instead of re-writing every feature for every dog breed or car model, we can create a `class` or `interface` which captures the common elements. 

The different varieties can now *inherit* these characteristics from that `class` or `interface` and make the necessary extensions or modifications.

This way, instead of writing every feature for each descendant, you can write the features once and use them many times.

### Two benefits of Inheritance

1. It reduces code size by eliminating redundancy.
2. Changes that affect multiple classes can be made in one place.

### Two drawbacks of Inheritance

1. Descendants (sub-classes or sub-interfaces) are dependent on their ancestors which makes it harder to re-use the descendants.
2. Changes to the ancestor will affect all of its descendants. This is the flip-side of the second benefit, so proceed with caution when practising this principle.

### Inheritance example in Java

Let's start with a basic Canine class that has some of the bare minimum requirements.


```java
class Canine {
    protected int numberOfLegs = 4; 
    protected boolean isCarnivore = true; 
}
``` 

Now let's create two descendants for this ancestor. 

One is called "GoldenRetriever" (because who doesn't like Golden Retrievers) and another is called "Wolf".


```java
class Canine {
    protected int numberOfLegs = 4; 
    protected boolean isCarnivore = true; 
}

class GoldenRetriever extends Canine {
    protected final boolean isCute = true; 
}

class Wolf extends Canine {
    protected String homeland = "Winterfell"; 
}
``` 

Both "GoldenRetriever" and "Wolf" sub-classes (descendants) will inherit the `numberOfLegs` and `isCarnivore` fields from their super-class(ancestor), the "Canine" class.

This benefit may seem trivial now, but on a larger scale, the effects of redundancy can become a nightmare.

## Conclusion

I hope these explanations were easy enough to follow. My next post will cover the Polymorphism OOP principle. 

Follow me on Twitter to get the heads up and thanks for reading.

If you liked this article, then please [support me] (https://www.buymeacoffee.com/hmcodes)