I've been learning Java for the first time for a while, so I'm learning the SOLID principle to improve my design skills.
I wrote an article to chew what I learned.
One of the SOLID principles, the principle and methodology for keeping software modules loosely coupled.
The following principles are stated.
A. Higher level modules should not depend on lower level modules. Both should rely on abstractions.
B. Abstraction must not rely on details. The details should depend on abstraction.
Quote: https://ja.wikipedia.org/wiki/Dependency Inversion Principle
In a nutshell, it's the connection between classes.
Let's give an example with a simple code.
public class Customer {
public void pay(){
BankingPayment payment = new BankingPayment();
payment.execute();
}
}
public class BankingPayment{
public void execute() {
//Bank payment processing
}
}
There are two classes, Customer and BankingPayment, and a BankingPayment instance is created inside the Customer class. It can be said that this state depends on the BankingPayment class for the Customer class.
In this state, when changing the BankingPayment class, the Customer class must be changed. (Case where the return value or argument of execute method is changed)
In other words, when the BankingPayment class (= lower level module) is changed, the Customer class (= upper level module) is also affected by the change.
If another payment method is required due to the addition of specifications or functions, it is necessary to change all parts of the Banking Payment class called in the Customer class in the previous example.
Don't rely on things that are easily changed, just rely on stable things
The interface is hard to change, but the implementation class is easy to change.
Therefore, it does not depend on the variable concrete (= implementation class), but on the stable interface (= abstract).
interface Payment{
void execute();
}
public class Customer {
public void pay(){
Payment payment = new BankingPayment();
payment.execute();
}
}
public class BankingPayment implements Payment{
@Override
public void execute() {
//Bank payment processing
}
}
In this state, the Customer class depends on the Payment interface, but it no longer depends on the BankingPament class. And the Payment interface is implemented by the BankingPament class.
In other words, it can be seen that the BankingPament class depends on the Payment interface owned by the upper level module (Custmer), and the dependency is reversed compared to the previous example.
Also, even if the payment method is changed from bank payment to credit payment in the previous example, the range of change in the Customer class can be minimized because the CreditPayment class can be newly implemented from the Payment interface.
By creating an interface and reversing the dependencies, it becomes possible to flexibly respond to changes in the system without relying on concrete modules that are easily changed.