Object-Oriented Design (OOD) helps us develop high-performance, scalable, and reusable programs. Among them, Ood has an important idea, that is, relying on the inverted principle (DIP), and extended concepts such as IOC, Di, and IOC containers.
This article describes four concepts with examples and provides sample code for the Java version.
Dependency inversion principle (DIP)
Dependency inversion is a software architecture design principle ,. The dependency inversion principle converts dependencies. The high-level module does not depend on the implementation of the lower-level module, and the lower-level module relies on the interfaces defined by the high-level module. In layman's terms, the high-level module defines the interface, and the lower-level module is responsible for implementation.
How can this problem be understood? For example.
Let's take a look at an example in life.
Figure 1 atm and bank card
I believe that most of my friends who have made money are deeply touched. If there is a card, the ATM of any bank can get the money. In this scenario, ATM is equivalent to a high-level module, while a bank card is equivalent to a low-level module. An ATM interface is defined for the insertion of all bank cards. That is to say, ATM does not depend on the specific bank card. It only needs to define the specification parameters (interfaces) of the bank card, and all the bank cards that have implemented the specification parameters can be used on the ATM. In real life, software development is even more so.
Dependency inversion (interfaces defined by high-level modules and implemented by lower-level modules)
In this figure, we find that the high-level module defines the interface and will no longer directly rely on the lower-level module. The lower-level module is responsible for implementing the interface defined by the high-level module. In this way, when a new low-level module is implemented, you do not need to modify the code of the High-level module.
Therefore, we can sum up the advantages of using dip:
The system is more flexible:You can modify part of the code without affecting other modules.
More robust system:You can modify part of the code without causing a system crash.
More efficient system:Components are loosely coupled and reusable to improve development efficiency.
Control inversion (IOC)
DIP is a software design principle. It only tells you how the two modules depend on each other, but it does not tell you how to do it. IOC is a software design pattern that tells you how to do it to remove the coupling between mutually dependent modules. Control inversion (IOC), which provides abstraction for mutually dependent components and transfers the acquisition of dependent (low-level module) objects to a third party (system) for control, that is, the dependent object is not directly obtained through new in the dependent module class.
Dependency injection (DI)
An important method of IOC is to transfer the creation and binding of dependent objects to the external part of the dependent object class. There are three main implementation methods: constructor injection, property injection, and interface injection.
The following uses the order stored in the database as an example to illustrate the problem using a Java example.
Assume that SQL Server is used at the initial stage of system development.
Public class sqlserver {
Public void add (){
System. Out. println ("addorder to SQL Server! ");
}
}
Define an order class:
Public class order {
Private sqlserver Ss = newsqlserver ();
Public void add (){
SS. Add ();
}
}
Test:
Public class main {
Public static void main (string [] ARGs ){
Order o = New Order ();
O. Add ();
}
}
Output:
It's okay here, but what if you need to add a MySQL database?
Create a database class to operate MYSQL:
Public class MySQL {
Public void add (){
System. Out. println ("add to mysqlserver ");
}
}
Order class needs to be modified:
Public class order {
Private MySQL Ss = new MySQL ();
Public void add (){
SS. Add ();
}
}
Test output:
What if more database operations are required? In this case, you still need to modify the code. Components are highly coupled and have poor scalability, which violates the dip principle.
Dependency injection solves the preceding problems.
There are three main implementation methods for dependency injection: constructor injection, property injection, and interface injection. The following is an example.
1. Constructor
Constructor injection is undoubtedly used to transmit dependencies through constructor. Therefore, the constructor parameters must be used to receive a dependent object. What is the parameter type? What is the type of the dependent object? Or an abstract type? According to the dip principle, we know that the high-level module should not depend on the lower-level module, and the two should depend on abstraction. The constructor parameter should be an abstract type.
Interface Definition:
Public interface IDB {
Publicvoid add ();
}
Then implement this interface in sqlserver
Publicclass sqlserver implements IDB {
@ Override
Public void add (){
// Todo auto-generatedmethod stub
System. Out. println ("addinto SQL Server ");
}
}
Modify the order class and inject objects into the constructor:
Public class order {
Private IDB;
Public Order (IDB ){
This. IDB = IDB;
}
Public void add (){
This. IDB. Add ();
}
}
Test class:
Public class main {
Publicstatic void main (string [] ARGs ){
Idbidb = new sqlserver ();
Ordero = New Order (IDB );
O. Add ();
}
}
Output:
By using constructor injection, we can transfer the creation and binding of the dependent object sqlserver to the external side of the Order class, thus removing the relationship between sqlserve and order classes. When we need to replace it with a MySQL database, we only need to redefine a MySQL class to implement the IDB interface. We need to create a new database where it is used and re-bind the dependency outside the order, you do not need to modify the order code.
For example:
Create a MySQL class:
Public class MySQL implements IDB {
@ Override
Publicvoid add (){
// Todo auto-generated method stub
System. Out. println ("addinto MySQL server! ");
}
}
In the main function, you only need to modify the following:
IDB = new MySQL ();
Test:
2. Property Injection
Property injection transmits dependencies through properties. Therefore, we first need to define an attribute in the dependency class order.
Public class order {
Private IDB; // Private Property
Public void add (){
This. IDB. Add ();
}
Public IDB getidb (){
Return IDB;
}
Public void setidb (IDB ){
This. IDB = IDB; // accept dependency
}
}
The interface class and database category class are the same as above. The main method needs to be written as follows:
Public class main {
Public static void main (string [] ARGs ){
IDB = new sqlserver ();
Order o = New Order ();
O. setidb (IDB );
O. Add ();
}
}
Test output:
Add into sqlserver
To expand the MySQL database, you only need to modify it in the main method:
Idbidb =NewMySQL ();
Test output:
Add into MySQL
3. Interface Injection
Compared with constructor injection and property injection, interface injection is somewhat complex and rarely used. The specific idea is to first define an interface, including a method for setting dependencies. Then the dependent class inherits and implements this interface.
First, define an interface:
Public interface idependent {
Publicvoid setdependency (IDB );
}
The dependency class implements this interface:
Public class order implements idependent {
Privateidb IDB;
@ Override
Publicvoid setdependency (IDB ){
// Todo auto-generated method stub
This. IDB = IDB;
}
Publicvoid add (){
This. IDB. Add ();
}
}
You also need to define the database access interface:
Public interface IDB {
Publicvoid add ();
}
The specific database implementation needs to implement this interface:
Public class sqlserver implements IDB {
@ Override
Publicvoid add (){
// Todo auto-generated method stub
System. Out. println ("addinto SQL Server ");
}
}
Test class:
Public class main {
Publicstatic void main (string [] ARGs ){
Ordero = New Order ();
Idbidb = new sqlserver ();
O. setdependency (IDB );
O. Add ();
}
}
Test output:
Add into sqlserver
To add MySQL access, you need to define MySQL implementation:
Public class MySQL implements IDB {
@ Override
Publicvoid add (){
// Todo auto-generated method stub
System. Out. println ("addinto MySQL ");
}
}
In the test class, you only need to make the following changes:
Idbidb =NewMySQL ();
Test output:
Add into MySQL.
IOC container
Simply put, it is to discard the manual method to create a dependency object and pass it to the dependent module. Instead, we select the di framework to create it for us to reduce our workload. For large projects, there are many mutually dependent components. If you still use a manual method to create and inject dependencies on your own, the efficiency is obviously very low, and there are often uncontrollable scenes. For this reason, the IOC container was born. The IOC container is actually a DI framework that simplifies our workload. It includes the following features:
L dynamically create and inject dependency objects.
L manage the object lifecycle.
L ing dependencies.
Spring is the most prominent in Java. More spring introductions will be shown in the following blog.
Non-unknown dip, IOC, Di, and IOC containers