OOPS concepts and Delegate Pattern

The program is not Object-oriented simply because it is using Inheritance, Polymorphism etc etc but what makes it truly Object oriented is leveraging those OO principles to make your code more effective.

The code should be:
1. Transparency: It should be obvious how the code works or if you go back and look at your code month back, and be able to figure out how the code works. e.g. Using meaningful method names, etc
2. Simplicity: Code should do exactly what is necessary and nothing more. Be careful as using design patterns may seem a good approach however they do add complexity. Instead go for the simpler code and write a simple code without any design pattern or any thing. But if something comes again and we need to modify the code, then follow the best design pattern for that purpose. This approach is also called “Fool me once”, as we need to modify our code only once, but then we have the design pattern in place. It seems to be a good idea because at times it is hard to figure out the perfect design pattern at the first go itself.
3. Design and Build incrementally: One small piece at a time. Either write a lot of code which may be more complex or design it incrementally. Say like put a framework in place and expand it as we proceed. And build code on top of that framework in an incremental way.

Cohesion and Coupling:

Coherence: It deals with the members, fields, methods, and the class itself shall focus on solving a very specific problem.
Coupling: When 2 things are connected to each other in an very indirect way, and when one thing changes other shall change as well e.g. a global variable if been shared by 2 classes is an example of coupling. When one method changes it, we shall need to look at the other one as well. This is a bad idea.
So the aim should be to Maximize cohesion and minimize coupling.

Methods vs Functions

Methods and functions are treated as the same thing, but they should not be.
Methods should be responsible for handling what the class is responsible to do i.e. what any other class sees. To do these things, these methods may need to have some code. For maintainability some code can be separated out of methods to form some functions. these are called functions.
Methods are all public facing, handling incoming message, responding in some way to it. 
Functions are just the code that do some arbitrary work.

What is an Object?

Object is a complete black box. They are not the data structures plus methods as we are taught in the notebooks. But the objects must be defined by what they do, and not what they contain. So its a complete black box, we don’t know how it works, but we can ask it to do something. We know what it can do, and what we can get in return, but we don’t know how it does. What goes inside it is unknown to us.

Using Inheritance

As we are taught, inheritance is used between the classes if and only if they passes the IS-A relationship test. It means only if one class does all that the other class do(or even something more) that it can extend that class.

Consider an example of phones. 
Shall Smartphone extend Phone class?
Lets see.
Phone class only deals with calls, receiving and doing it.
SmartPhone has lots of things to do, but the calls thing is one of them. 
So YES SmartPhone IS-A Phone and we can extend Smartphone with Phone.

Take another example shall Manager extend Employee class?
Lets see.
Employee do the task, and the Manager approves/rejects it. 
Although both are an employee for the firm they are working in, but the nature of their task is not the same.
For a laymen terms, Manager is also an Employee. But we shall not extend the Manager by Employee, as their responsibilities are different.

So whenever extends is to be used, keep the L(Liskov Substitution Principle) of SOLID in mind that the super-class and subclass should be interchangeable with each other.

Also apart from this, inheritance can be a problem where changes to a super-class can break the sub-classes. This is called Fragility
Also if any class extends a class, then it can not extend any different class, as java being single inheritance based. So its a good practice to leave the class an option to extend something, unless and until it does not have a option.

So its better to avoid extends and use interfaces instead, or if that is not possible make the classes final, or if that is also not possible make the “dangerous” methods final.

Interfaces:

It is a contract that the methods have to be implemented by which class that implements that interface.
General rule that whenever 2 or more classes must have similar(or even identical) method/s, that method/s belongs to the super class because we don’t want those methods to be written in all classes, but one.

Using abstract classes we get the advatage that we have the default implementation in the base abstract class, whereas we lose this with the interfaces.

abstract class IPay {

void initPaymentGateway() {
// Some Code.
}

abstract void pay();
}

class CreditCardPayment extends IPay {

@Override
public void pay() {

}
}

class DebitCardPayment extends IPay {

@Override
public void pay() {

}
}

So out payment need to have init and pay methods. As initPaymentGateway is common to all so it makes sense to have its implementation at one place, so its in their super class. And as pay method depends on the type of payment, its implementation differs.

As discussed above, can we avoid extends here?

One way can be to have the common code out in some utility, and can use interfaces like:

interface IPay {
void pay();
}

class PaymentUtils {
public static void initPaymentGateway() {
// Some code
}
}

class CreditCardPayment implements IPay {

@Override
public void pay() {

}
}

class DebitCardPayment implements IPay {

@Override
public void pay() {

}
}

It may seem okay, but it is not the correct design. Utility classes are sort of the hacks which the developers provide while coding. So STAY AWAY from such classes like utilities, Constants etc.

A better approach can be to use interfaces with the default implementation inside the static class in it, like:

interface IPaymentGateway {
void init();

static class Implementation implements IPaymentGateway{
public void init() {
// Some code
}
}
}

class CreditCardPayment implements IPaymentGateway{

private IPaymentGateway delegate =
new IPaymentGateway.Implementation();

@Override
public void init() {
delegate.init();
}
}

class DebitCardPayment implements IPaymentGateway{

private IPaymentGateway delegate =
new IPaymentGateway.Implementation();

@Override
public void init() {
delegate.init();
}
}

This is also a kind of a design pattern, also known as delegate pattern. Here we have delegated the work(implementation) to something else(interface’s inner class).
This pattern is mostly useful when we are confused between the abstract class to use or the interfaces. 
We can change our abstract class to interfaces without breaking a single code using this, as now all the implemented methods in abstract classes can be contained in Interfaces.