Single Responsibility Principle
The definition of the single responsibility principle is that for a class, there should be only one reason for its change. In other words, a class should only be responsible for one thing. If a class is responsible for two different things, method M1 and method M2, when the M1 method changes, we need to modify the M1 method of this class, but at this time, the M2 method may not work. This is not what we expected, but it is very possible due to this design. So at this time, we need to separate the M1 method and M2 method into two classes. Let each class focus only on its own methods.
The benefits of the single responsibility principle are as follows:
It can reduce the complexity of the class. Each class is only responsible for one responsibility. This makes the logic much simpler and improves the readability of the class and the maintainability of the system, because there will be no other strange methods that interfere with our understanding of the meaning of this class. When changes occur, the impact of the changes can be minimized because modifications will only be made in this class.
Opening and closing principle
The open-closed principle, like the single responsibility principle, is a very basic and generally common sense principle. The definition of the open-closed principle is that objects (classes, modules, functions, etc.) in software should be open for extension, but closed for modification.
When the requirements change, we need to modify the code. At this time, we should try to extend the original code instead of modifying the original code, because this may cause more problems.
This principle is the same as the single responsibility principle. It is a principle that everyone thinks so but does not specify how to do it.
We can ensure the opening and closing principle in one way. We use abstraction to build the framework and implementation to extend the details. In this way, when modification occurs, we directly use abstraction to derive a concrete class to implement the modification.
Richter substitution principle
The Liskov substitution principle is a very useful concept. His definition
If for every object o1 of type T1, there is an object o2 of type T2, so that the behavior of all programs P defined with T1 does not change when all objects o1 are replaced with o2, then the type T2 is a subtype of type T1.
It’s a bit complicated to say this, but there is actually a simple definition
All references to a base class must be able to transparently use objects of its subclasses.
In layman's terms, the Liskov substitution principle is: subclasses can extend the functions of the parent class, but they cannot change the original functions of the parent class. It contains the following meanings:
Subclasses can implement abstract methods of the parent class, but they cannot override non-abstract methods of the parent class.
Subclasses can add their own unique methods.
When a subclass method overrides a parent class method, the formal parameters of the method are looser than the input parameters of the parent class method.
When a method of a subclass implements an abstract method of a parent class, the return value of the method is more stringent than that of the parent class.
The reason why the Liskov Substitution Principle requires this is because inheritance has many shortcomings. Although it is a method of reusing code, inheritance violates encapsulation to a certain extent. The attributes and methods of the parent class are transparent to the subclass, and the subclass can modify the members of the parent class at will. This also causes that if the requirements change and the subclass overrides some methods of the parent class, other subclasses will not be able to work properly. So the Richter substitution rule was proposed.
Ensuring that the program follows the Liskov substitution principle requires our program to establish abstractions, establish specifications through abstraction, and then use implementation to extend the details. Is this familiar? Yes, the Liskov substitution principle and the opening and closing principle are often interdependent.
Dependency inversion principle
The dependency inversion principle refers to a special way of decoupling, so that high-level modules should not depend on the implementation details of low-level modules, and dependent modules are inverted. This is also a difficult definition to understand. It can be simply put as
High-level modules should not depend on low-level modules, both should depend on their abstractions. Abstractions should not depend on details. Details should depend on abstractions
In Java, abstraction refers to interfaces or abstract classes, neither of which can be instantiated. The details are the implementation classes, which are classes that implement interfaces or inherit abstract classes. It can be instantiated. The high-level module refers to the calling end, and the low-level module is the specific implementation class. In Java, the principle of dependency inversion means that dependencies between modules occur through abstraction. There is no direct dependency between implementation classes, and their dependencies are realized through interfaces. This is commonly known as interface-oriented programming.
We have an example below to illustrate this problem. This example is of a worker using a hammer to repair something. Our code is as follows:
public class Hammer {
public String function(){
return "Use a hammer to repair things";
}
}
public class Worker {
public void fix(Hammer hammer){
System.out.println("Worker" hammer.function());
}
public static void main(String[] args) {
new Worker(). fix(new Hammer());
}
}
This is a very simple example, but if we want to add a new function, workers use screwdrivers to repair things. In this class, we find that it is difficult to do. Because our Worker class depends on a specific implementation class Hammer. So we use the idea of interface-oriented programming and change it to the following code:
public interface Tools {
public String function();
}
Then our Worker relies on other detail classes through this interface. code show as below:
public class Worker {
public void fix(Tools tool){
System.out.println("Worker" tool.function());
}
public static void main(String[] args) {
new Worker(). fix(new Hammer());
new Worker(). fix(new Screwdriver());
}
}
Our Hammer class and Screwdriver class implement this interface
public class Hammer implements Tools{
public String function(){
return "Use a hammer to repair things";
}
}
public class Screwdriver implements Tools{
@Override
public String function() {
return "Use a screwdriver to repair something";
}
}
In this way, through interface-oriented programming, our code has high scalability, reduces the coupling between codes, and improves the stability of the system.
Interface isolation principle
The definition of interface isolation principle is
The client should not rely on interfaces it does not need
In other words, the dependency relationship between classes should be established on the smallest interface. It seems more difficult to understand. Let's illustrate with an example. We know that if a concrete class implements an interface in Java, it must implement all methods in the interface. If we have a class A and class B that depend on interface I, class B is the implementation of class A's dependency, and this interface I has 5 methods. But classes A and B only depend on methods 1, 2, and 3, and then classes C and D depend on interface I. Class D is an implementation of dependency on class C, but they depend on methods 1, 4, and 5. Then when implementing the interface, class B must implement method 4 and method 5 that it does not need, and class D must implement method 2 and method 3 that it does not need. This is simply a disaster design.
So we need to split the interface, that is, divide the interface into the smallest interface that satisfies dependencies. Class B and class D do not need to implement interface methods that have nothing to do with them. For example, in this example, we can split the interface into three. The first one is an interface that only contains method 1, the second interface contains methods 2 and 3, and the third interface contains methods 4 and 5. In this way, our design satisfies the interface isolation principle.
The above design ideas can be formed into SOLID with the first letters of English. Programs that meet these five principles are also said to meet the SOLID criteria.
Dimit Principle
The Dimit Principle is also called the Minimum Knowledge Principle, and his definition is
An object should keep minimal knowledge of other objects.
Because the closer the relationship between classes is, the greater the degree of coupling. When one class changes, the greater the impact on the other class. Therefore, this is also the general principle of software programming we advocate: low coupling, high coupling. Cohesion. There is a simpler definition of Demeter's Law
Only communicate with direct friends. First, let’s explain what a direct friend is: each object will have a coupling relationship with other objects. As long as there is a coupling relationship between two objects, we say that the two objects are friends. There are many ways of coupling, such as dependency, association, combination, aggregation, etc. Among them, we call classes that appear in member variables, method parameters, and method return values direct friends, while classes that appear in local variables are not direct friends. In other words, it is best not for unfamiliar classes to appear inside the class as local variables.
Here we can use a real-life example to explain. For example, if we need a CD, we may go to the video store and ask the boss if he has the CD we need. The boss says that he doesn't have the CD we need now, and you can just come and get it when it is available. Here we don’t need to care about where or how the boss got the CD. We only communicate with the boss (direct friend). As for the conditions under which the boss got the CD from his friend, we don’t care. We don’t agree with him. The boss’s friends (strangers) communicate. This is an application of Dimit. To put it bluntly, it is an intermediary method. We use the boss as an intermediary to contact the person who actually provides the CD.
The above is the detailed content of What are the six principles of object-oriented design in Java. For more information, please follow other related articles on the PHP Chinese website!