Explaining Abstraction - One of the Basic Principals of OOP
Tags: abstraction, typescript, oop, learning
Published: 2022-10-23 16:37:49

Abstraction is one of the basic principals of Object-Oriented Programming, and one which is super easy to understand.

## What exactly is Abstraction?
In a nutshell, abstraction involves hiding the complicated logic of an object from its users, and only providing the required inputs and outputs.

It's a very common practice, and one which every single software developer will have come across at some stage. I think we are all familiar with `print('Hello World!)`or `console.log('Hello World!')`. Its probably the first thing every developer learns, and it's a perfect example of abstraction usage. You don't need to know the inner details of how the `print()` function works here, but you just know that calling it while passing in a string will print the message. The hidden (and actually quite complex) logic of printing the string message is of no concern to the user and has been abstracted away from them.

## Why use Abstraction?
The usage of abstraction and it's benefits are pretty clear from its definition - reducing complexity for users. Abstraction's key benefit is making our code simple and easy to use when implemented.
Abstraction is also important in the sense that it also allows users to implement more logic on top of an abstraction, without needing to think about all the hidden complexity.

## When to use Abstraction?
Abstraction is most commonly used in two scenarios:

1. When we want to share some common behavior
2. Using the Template Method Pattern

Sharing common behavior is the most widely used usage of Abstraction. Lets take a look at how we can use abstraction in order to share common behaviour.

## Example of Abstraction

Using TypeScript, I am going to create an abstract class called `Vehicle` which, when instantiated, will set the number of wheels and max speed of the object (in both km and miles). It also has three functions, one for returning the number of wheels, one for returning the max speed in km, and one for returning the max speed in miles.

```typescript
abstract class Vehicle {
    private noWheels: number;
    private maxSpeed: number;
    private readonly KM_TO_MILE_CONVERSION = 1.609;

    constructor(noWheels: number, maxSpeedKilometers: number) {
        this.noWheels = noWheels;
        this.maxSpeed = maxSpeedKilometers;
    }

    public getNoWheels(): number {
        return this.noWheels;
    }

    public getMaxSpeedKilometers(): number {
        return this.maxSpeed;
    }
  
   public getMaxSpeedMiles(): number {
        return 
             parseFloat(
                    (this.maxSpeed/this.KM_TO_MILE_CONVERSION)
            .toFixed(2));
   }
}
```
As you can see, the function for returning the max speed in miles has some additional logic tied to. We are diving the maxSpeed in km by the conversion number, before rounding the number to 2 decimal places, and parsing it back to float. In abstraction terminology, this is what we refer to as the "complicated logic", which abstraction is hiding from the end user.

Lets take a look at a child class, or a user of this abstract class below:

```typescript
export class Car extends Vehicle {

    private carMake: string;

    constructor(numberOfWheels: number, maximumSpeed: number, 
                carMake: string){
        super(numberOfWheels, maximumSpeed);
        this.carMake = carMake;
        this.printMaxSpeedInMiles();
    }

    public printMaxSpeedInMiles(): void {
      // Max Speed of the Car in Miles from the Parent Object
      console.log('The Max Speed of the Car in Miles is: ' + 
      this.getMaxSpeedMiles());
    }
}
```
We can see that this class utilizes the abstract class due to the use of the `implements` keyword. This, alongside calling the `super()` method, gives us access to the abstract classes functions. We can see this by the fact that within `printMaxSpeedInMiles()` we are able to call the `Vehicle` function `getMaxSpeedMiles()`, without defining it ourselves.

What this all means is that if we then instantiate a new object of type `Car`:
`new Car(2, 175, 'hyundai');`

We will automatically get the max speed per miles printed to the console:
`The Max Speed of the Car in Miles is: 108.76`

The benefit here, of course, is that we can have multiple 'child' classes which are abstracted from `Vehicle`. We could have a class `Bicycle` or `Motorcyle` and we would also have the ability to retrieve the max speed of either in km or miles, alongside adding additional functionality on top of this. Of course, this is a very basic example, but hopefully you get the picture.

_**So there you have it, a basic example of one of the key principals of OO Programming - Abstraction**_