OOP Principles For Dummies
Tags: computerscience, beginners, programming, java
Published: 2021-03-21 06:48:23

I don't have much experience but been in a couple of interviews. 

They seem to always ask the same cliche questions.

For example:

> Where do you see yourself in five years?

I usually have answers prepared beforehand, for these types of questions. 

But there is one question, that I know will come up and I keep messing it up.

It is:

> What are the main principles of Object-Oriented Programming?

It's pretty much a universal question, talked about in all sorts of technical interviews, from junior to senior. Interviewers ask this type of question because it is an easy way to gauge the interviewee's experience and level of understanding.

It helps to tell three things:

1. **Did the interviewee prepare for the interview?**

    If you hear an answer immediately, it's usually a good sign.

2. **Does the interviewee think properly before he writes code?**

    Understanding and applying the principles of OOP shows that the candidate is beyond the point of hack and slash coding. He doesn't just copy code from the internet, he has structure in his code and way of thinking.

3. **Is the interviewee understanding deep or shallow?**

    This is a very open-ended question, you can go as shallow or as deep as you can. Bonus point if you go deep.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwn68zhjzzm9ibf128mk.png)

The four principles object-orientated programming are **encapsulation**, **abstraction**, **inheritance**, and **polymorphism**.

I know it sounds very intimidating but trust me, it's not. 

It's much simpler, and that's why I want to give you simple, straight-to-the-point explanations with examples on each.

## Encapsulation

> Encapsulation binds data and it's related methods together within a class. It also protects the data by making fields private and giving access to them only through their related methods.

Imagine we have a small program. 

It has a few objects which talk to each other, within the rules defined in the program.

Encapsulation is achieved when each object keeps its state private meaning that no outside class can directly change it. Instead, they can only call a list of public function — called methods.

Before we move on we have introduced some keywords which you may be confused about, let me explain:

1. **State**: The state represents the data (value) of an object. Suppose a bank account is an object then Account Number, Amount, etc... will be the states of my bank account.
2. **Private**: Data that is can only be accessed within the class. 
3. **Protected**: Data that can only be accessed within the class, and it's subclasses.
4. **Public**: Data or functions (methods) which can be accessed outside of the class.

### Violation of Encapsulation

Imagine that we have a single-player role-playing game with a class that manages quests called `Quest`. 

```java
public class Quest {
    // possible options for status
    private static final int NOT_PICKED = 0;
    private static final int PICKED = 1;
    private static final int COMPLETED = 2;
    
    // state
    public String title;
    public int reward;
    public int status;
    
    // Constructor
    public Quest(String title, int reward, int status){
        this.title = title;
        this.reward = reward;
        this.status = status;
    }
    
    // Other behavior
}
```

Our client code will look something like this:

```java
public static void client() {
    Quest quest = new Quest('Goblin Slaying', 190, 3);
}
```

Imagine that one of the players got access to this client, and wanted to cheat by changing the game status and reward.

```java
public static void client() {
    Quest quest = new Quest('Goblin Slaying', 190, 3);
    quest.reward = 1000000000;
    quest.status = 5;
}
```

Right now this code is valid, because our variables are publicly accessible. Another problem here is that our "hacker" set the status to 5 which doesn't exist, and thus our game breaks.

### Fixing Our Example

The way to fix this is to simply make all our variables private and only accessible through their respective methods.

```java
public class Quest {
    private static final int NOT_PICKED = 0;
    private static final int PICKED = 1;
    private static final int COMPLETED = 2;

    private String title;
    private int reward;
    private int status;

    public Quest(String title, int reward, int status){
        this.title = title;
        this.reward = reward;
        this.status = status;
    }

    public String getTitle() {
        return title;
    }

    public int getReward() {
        return reward;
    }

    public int getStatus() {
        return status;
    }

    public void setQuestNotPicked(){
        this.status = NOT_PICKED;
    }

    public void setQuestPicked(){
        this.status = PICKED;
    }

    public void setQuestCompleted(){
        this.status = COMPLETED;
    }

    // other behavior

}
```

With these changes now, we cannot change the reward and title. But we can get them through their respective functions. 

Finally, our code is "hacker" safe.

### Benefits of Encapsulation

- Encapsulation protects an object from unwanted access by clients.
- Encapsulation allows access to a level without revealing the complex details below that level.
- It reduces human errors.
- Simplifies the maintenance of the application
- Makes the application easier to understand.

### Tips on encapsulation

For the best encapsulation, object data should almost always be restricted to `private` or `protected`. If you choose to set the access level to `public`, make sure you understand the consequences of the choice.

## Abstraction

> Abstraction is the concept of object-oriented programming that "shows" only essential attributes and "hides" unnecessary information.

Abstraction is an extension of encapsulation, but what's the difference?

- **Encapsulation -** means hiding data like using `getter` and `setter` etc.
- **Abstraction** - means hiding implementation using abstract class and interfaces etc.

But now I'm even more confused, what are abstract classes and interfaces?

### Abstract Class

An abstract class is a simply a normal class that has some special exceptions:

- An abstract class *may* have methods that do not have a body
- An abstract class has to be sub-classed, at some level, to a non-abstract class before you can instantiate an object
- It is not allowed to **directly** instantiate an object of an abstract class

So, why would I ever use such classes?

Well, they allow us to do multiples things:

1. Consolidate common behavior (unlike an interface which only defines the contract)
2. Provide default (and optionally override-able) implementations for functions

Let's look at a simple example:

```java
//abstract parent class
abstract class Animal{
   //abstract method
   public abstract void sound();
}
//Dog class extends Animal class
public class Dog extends Animal{

   public void sound(){
	System.out.println("Woof");
   }
   public static void main(String args[]){
	Animal obj = new Dog();
	obj.sound();
   }
}
```

### Abstract class vs Concrete class

1. An abstract class is useless until it is extended by another class.
2. If you declare an **abstract method** in a class then you must declare the class abstract as well. You can’t have an abstract method in a concrete class. Its vice versa is not always true: If a
class is not having any abstract method then also it can be marked as abstract.
3. It can have non-abstract method (concrete) as well.

### Interfaces

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5oq7i88uip37ksdue5v.png)

> An interface is an abstract type that is used to specify a behavior that classes must implement.

Think of interfaces as a contract.

You have a bunch of rules which you must follow through but the details don't really matter. 

For example, we have a contract for the behavior of a dog called `DogInterface` it will look something like this:

```java
public interface DogInterface {
    public void bark();
    public void wiggleTail();
    public void eat();
}
```

Here we are saying that every class that implements the `DogInterface` must have these three methods. As you can see there is absolutely no implementation details, it doesn't say how should a dog bark, wiggle its tail or eat. Having said that, we now have the ability to create different dog classes that all follow the same contract.

```java
public class LabradorRetriever implements DogInterface {
    
    public void bark() {
        System.out.println("Woof, woof!!");    
    }
    
    public void wiggleTail() {
        System.out.println("wiggles tail");
    }
    
    public void eat() {
        System.out.println("eats food");
    }
}
```

You might ask "Why would I ever want to use an interface?"

Interfaces in a nutshell guarantee that a class will have *x* methods. 

This may sound too simple, but it actually is just that. 

Nonetheless this small feature helps us tremendously, imagine we have a shipping service program and our client code uses the `Car` class to transport stuff. After some time our program gets popular and we know need to support planes, trains, ships, trucks, etc...

One way to do this is to create a common interface called `TransportInterface` that will be used by all the transport vehicles (airplane, car, etc...). This way our client code will not have to worry if the class `Airplane` will not have some common method.

### Which to use? Abstract Classes or Interfaces?

As always it depends.

There are many different factors to consider.

But in general, an abstract class is used when you want a functionality to be implemented or override in subclasses. On the other hand, an interface will only allow you to describe functionality but no implementation. Also in most programming languages, a class can only extend one abstract class, but it can take advantage of multiple interfaces.

### Coming back to abstraction

This post definitely took a turn, but it was necessary to talk about abstract classes and interfaces. 

In a nutshell:

Abstraction is an extension of encapsulation where it literally hides implementation using either abstract classes or interfaces.

## Inheritance

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4y2cjves5e9y1lsv1f8.png)

> Inheritance is the mechanism of basing an object or class upon another object or class, retaining similar implementation.

A common problem in programming is that objects are too damn similar. 

Objects are often very similar, have common functionality but they are not entirely the same. 

Now comes the dilemma that every programmer goes through.

"*Do I create an entirely new class or put all common functionality in a base class?"*

I'm not gonna say which option is better, but we are talking about OOP today. That means we are gonna pick the second option, creating a base class that will store all common functionality.

### School Example

Imagine we were tasked to create a school management system and we have two entities called Student and Teacher. In our first version of the app we implemented it them like this:

```java
public class Student {
    private String name;
    private String last_name;
    private String gender;
    private String classroom;
    private String year;

    public void attendClass(){
        System.out.println("Attend Class");
    }

    public void eatSandwich(){
        System.out.println("Eating a sandwich");
    }
}
```

```java
public class Teacher {
    private String name;
    private String last_name;
    private String gender;
    private String schedule;
    private String subject;

    public void teachClass(){
        System.out.println("Teaching a class");
    }

    public void eatSandwich(){
        System.out.println("Eating a sandwich");
    }
}
```

As you can see there are many common states and behaviors, how about we extract all that to a common class called `Human`

```java
public class Human {
    protected String name;
    protected String last_name;
    protected String gender;

    public void eatSandwich(){
        System.out.println("Eating a sandwich");
    }
}
```

*PS. I set all my variables protected for them to be accessible in subclasses.*

With this we can inherit all common states and behaviors into our `Student` and `Teacher` class.

```java
public class Student extends Human {
    private String classroom;
    private String year;

    public void attendClass(){
        System.out.println("Attend Class");
    }
}
```

```java
public class Teacher extends Human {
    private String schedule;
    private String subject;

    public void teachClass(){
        System.out.println("Teaching a class");
    }
}
```

Congratulations we have removed duplicated code, and that is a good sign.

### Coming back to inheritance

Inheritance isn't that simple, there are actually different types of inheritance.

1. **Single Inheritance**: This is the simplest kind of inheritance where we inherit from a parent class. Just like our example above with teachers and students.
2. **Multi-Level Inheritance**: An inheritance becomes multi-level when it inherits from a subclass. For example, let's imagine from our example above that we added a class called `Intern`. Intern has the same state and behavior as `Teacher` but with some extra fields, so we simply inherit from `Teacher`. This is multiple inheritances because now class `Intern` inherits from both `Teacher` and `Human`.
3. **Multiple Inheritance**: Happens when a class inherits more than one parent class. This is usually not allowed in most programming languages but is available in some. 
4. **Hierarchical inheritance**: Is when multiple child classes inherit from the same parent class. For example we have a class `Dog` and `Cat` that both inherit from class `Animal`.
5. **Hybrid Inheritance**: When there is a combination of more than one form of inheritance, it is known as hybrid inheritance.

### Benefits of Inheritance

1. Code reusability
2. One superclass can be used for the number of subclasses in a hierarchy.
3. No changes to be done in all base classes, just do changes in parent class only.
4. Inheritance avoids duplicity and data redundancy.
5. Inheritance is used to avoid space complexity and time complexity.

### Precaution

Now I am not saying to always extend your classes, doing that will make your code very coupled together. Another approach would be to use composition or aggregation. 

You can read more about it [here](https://javarevisited.blogspot.com/2014/02/ifference-between-association-vs-composition-vs-aggregation.html)

## Polymorphism

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/daq53p06dkwwznee3pp2.png)

> Polymorphism is the ability of an object to take on many forms.

Let us break down the word:

- Poly = many: polygon = many-sided, polystyrene = many styrenes , polyglot = many languages, and so on.
- Morph = change or form, morphology = study of biological form, Morpheus = the Greek god of dreams able to take any form.

So polymorphism is the ability (in programming) to present the same interface for differing underlying forms (data types).

The classic example is the `Shape` class and all the classes that can inherit from it (square, circle, dodecahedron, irregular polygon, splat and so on).

With polymorphism, each of these classes will have different underlying data. A point shape needs only two co-ordinates (assuming it's in a two-dimensional space of course). A circle needs a center and radius. A square or rectangle needs two coordinates for the top left and bottom right corners and (possibly) a rotation. An irregular polygon needs a series of lines.

By making the class responsible for its code as well as its data, you can achieve polymorphism. In this example, every class would have its own `Draw()` function and the client code could simply do:

```java
shape.Draw()
```

to get the correct behavior for any shape. This is in contrast to the old way of doing things in which the code was separate from the data, and you would have had functions such as `drawSquare()` and `drawCircle()`.

### Types of Polymorphism

Usually in interviews they ask questions like:

*"What is polymorphism?"*

You answer them properly, then to throw you off your game they ask:

*"What are the different types of polymorphism?"*

Now you have two options:

1. Answer the question cause your smart.
2. Make up some stuff.

Unfortunately, I had this same question asked me, and I failed to answer. This is why we will talk about the types of polymorphism.

The two types of polymorphism are **dynamic polymorphism** and **static polymorphism**.

### Dynamic Polymorphism

This is also known as: 

- Run-Time polymorphism
- Dynamic binding
- Run-Time binding
- Late binding
- Method overriding

This is your standard polymorphism. It's basically when a subclass has a method with the same name and parameters of a method in a parent class, these methods are in different forms (this is known as method overriding).

For example, let us illustrate our shapes example above.

```java
public class Shape {
    protected int width;
    protected int height;

    // some more functionality for shape

    public void draw(){
        // draws a shape using the width and height.
    }
}
```

```java
public class Circle extends Shape {
    private int radius;

    @Override
    public void draw() {
        // draw a circle code
    }
}
```

```java
public class Square extends Shape {

    @Override
    public void draw() {
        // specifically draw a square
    }
}
```

Our client code will look something like this:

```java
public static void main(String[] args) {
        Shape shape = new Shape();
        Shape circle = new Circle();
        Shape square = new Square();
        
        shape.draw(); // calls the draw method in shape class
        circle.draw(); // calls the draw method in circle class
        square.draw(); // calls the draw method in square class
    }
```

As you can see, both our `Circle` and `Square` class inherit from `Shape` class. They also override the `draw()` method in `Shape` class.

### Static Polymorphism

This is also known as:

1. Compile-Time polymorphism
2. Static binding
3. Compile-Time binding
4. Early binding
5. Method overloading

This is basically when a method has many forms in the **same** class.

Let's look at an example:

```java
public class Calculator {
    void add(int a, int b) {
        System.out.println(a+b);
    }
    void add(int a, int b, int c) {
        System.out.println(a+b+c);
    }
}
```

```java
public static void main(String args[]) {
        Calculator calculator = new Calculator();
        
        // method with two parameters gets called
        calculator.add(10, 20); // output: 30
        
        // method with three parameters get called
        calculator.add(10, 20, 30); //output: 60
    }
```

### Benefits of Polymorphism

- It helps the programmer to reuse the codes, i.e., classes once
written, tested and implemented can be reused as required. Saves a lot
of time.
- Single variable can be used to store multiple data types.
- Easy to debug the codes.

## Conclusion

The four principles of object-orientated programming are:

1. **Encapsulation**:  means hiding data like using `getter` and `setter`
2. **Abstraction**: hides implementation using interfaces and abstract classes.
3. **Inheritance**: mechanism of basing an object or class upon another object or class, retaining similar implementation.
4. **Polymorphism**: ability of an object to take on many forms.

Congratulations, now you can answer one of the most asked questions in interviews and kick ass.

If you got any questions, then feel free to leave them down in the comments section below.