Single responsibility principle

A class or module should have one and only one reason to change.

advantages

  1. Reduce class complexity
  2. Provides readability of classes and improves system maintainability
  3. Risk reduction due to change
  4. Reduce coupling

implementation

Class T is responsible for two different responsibilities: responsibility P1 and responsibility P2. When class T needs to be modified due to a change in the requirements of responsibility P1, it is possible that the function of responsibility P2, which is normally functioning, will fail.

The solution: Follow the single responsibility principle. Establish two classes T1 and T2 respectively, so that T1 completes the function of responsibility P1 and T2 completes the function of responsibility P2. This way, when class T1 is modified, responsibility P2 is not at risk of failure; Similarly, when T2 is modified, there is no risk of failure in responsibility P1.

The open closed principle

Definition: software entities such as classes, modules, and functions should be open for extension and closed for modification.

advantages

  1. Reduce coupling of various parts of the program
  2. Improve code reusability
  3. Improve software maintainability

implementation

  • Cause of the problem: During the software life cycle, when the original code needs to be modified due to changes, upgrades and maintenance, errors may be introduced into the old code, or we may have to reconstruct the whole function, and the original code needs to be re-tested.
  • Solution: When software needs to change, try to do so by extending the behavior of entity classes rather than by modifying existing code

The key to solve the problem lies in abstraction, which is the first core essence of object-oriented design. In object-oriented, through abstract classes and interfaces, the characteristics of concrete classes are defined as the abstraction layer, which is relatively stable, so as to meet the requirement of “close to modification”. Concrete classes derived from abstract classes can change the behavior of the system so that it is “open to extension”

The instance

The open and closed principle is probably the most ill-defined of the six principles of design patterns. It tells us only that we should be open to extension and closed to change, but how exactly can we be open to extension and closed to change?

Build the framework with abstractions and extend the details with implementations.

Interface Isolation Principle

A client should not rely on interfaces it does not need; The dependency of one class on another should be based on the smallest interface.

The principle of interface isolation is as follows: Create a single interface instead of a large and bloated interface, refine the interface as much as possible, and contain as few methods as possible.

The instance

Design that does not follow interface isolation principles

Design to follow interface isolation principles

Dependency inversion principle

A high-level module should not depend on a low-level module; both should depend on its abstraction; Abstractions should not depend on details, details should depend on abstractions.

The core idea is: program to the interface, not to the implementation.

Here, abstraction refers to an interface or abstract class, while detail refers to a concrete implementation class.

advantages

  1. Reduce coupling between classes
  2. Improve system stability
  3. Reduce the risk of modifying the program

implementation

  • Class A depends directly on class B. If you want to change class A to depend on class C, you must modify the code of class A to do so. In this scenario, class A is typically A high-level module responsible for complex business logic. Classes B and C are low-level modules responsible for basic atomic operations; If class A were modified, it would introduce unnecessary risk to the program.
  • Solution: Change class A to depend on interface I. Class B and C implement interface I respectively. Class A indirectly relates to class B or C through interface I, which greatly reduces the probability of changing class A. Coupling in an abstract way is key to the dependency inversion principle. Abstract coupling always involves the inheritance of a concrete class from an abstract class, and the need to ensure that any reference to the base class can be changed to its subclasses. Therefore, the Richter substitution principle is the basis of the dependency inversion principle.

code

Scene: A mother is telling a story to her child. Just give her a book and she can follow the book to tell a story to her child.

class Book {
    public String getContent() {
        return "Once upon a time......"; }}class Monther {
    public void narrate(Book book) {
        System.out.println("Mom starts telling stories.");
        System.out.println(book.getContent());
    }
}

public class DIPClient {
    public static void main(String[] args) {
        Monther monther = new Monther();
        monther.narrate(newBook()); }} Output: Mom started telling stories a long time ago......Copy the code

If you need to talk about the content in the Newspaper, you need to create a new class Newspaper

class Newspaper {
    public String getContent() {
        return "Financial storm rolls earth......"; }}class Book {
    public String getContent() {
        return "Once upon a time......"; }}class Monther {
    public void narrate(Book book) {
        System.out.println("Mother begins to tell a story from a book.");
        System.out.println(book.getContent());
    }
    // Modify the Mother class.
    public void narrate(Newspaper newspaper) {
        System.out.println("Mom starts talking about what's in the paper.");
        System.out.println(newspaper.getContent());
    }
}

public class DIPClient {
    public static void main(String[] args) {
        Monther monther = new Monther();
        monther.narrate(new Book());

        monther.narrate(newNewspaper()); }} Output: Mother began to tell the story of the book a long time ago...... Mother began to speak about the contents of the newspaper financial storm and soil to......Copy the code

It doesn’t make sense to create a new class for another headline and then change the Monther class. At this point, create a new interface class, so that it depends onthe interface, rather than the concrete implementation class, to achieve the purpose of Monther class need not modify.

interface IReader {
    String getContent();
}

class Newspaper implements IReader {
    @Override
    public String getContent() {
        System.out.println("Mom starts talking about what's in the paper.");
        return "Financial storm rolls earth......"; }}class Book implements IReader {
    @Override
    public String getContent() {
        System.out.println("Mother begins to tell a story from a book.");
        return "Once upon a time......"; }}class Monther {
    public void narrate(IReader reader) {
        System.out.println(reader.getContent());
    }
}

public class DIPClient {
    public static void main(String[] args) {
        Monther monther = new Monther();
        monther.narrate(new Book());
        monther.narrate(newNewspaper()); }} Output: Mother began to tell the story of the book a long time ago...... Mother began to speak about the contents of the newspaper financial storm and soil to......Copy the code

Richter’s substitution principle

All references to base classes (superclasses) must be able to transparently use objects from their subclasses. A subclass can extend the functionality of its parent class, but cannot change the functionality of its parent class.

implementation

  • Problem: There is A function P1, which is done by class A. Function P1 needs to be extended, and the expanded function is P, where P consists of the original function P1 and the new function P2. If new function P is completed by subclass B of class A, subclass B may fail original function P1 while completing new function P2.
  • Solution: When using inheritance, follow the Richter substitution principle. When class B inherits from class A, do not overwrite the methods of the parent class A or override the methods of the parent class A, except adding new methods to complete the new function P2.

The instance

Give an example of two numbers subtracting from each other

class A {
    public int func1(int a, int b) {
        returna-b; }}public class LSPClient {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("5-3 =" + a.func1(5.3));
        System.out.println("The 100-10 =" + a.func1(100.10)); }} Output:5 - 3 = 2
100 - 10 = 90
Copy the code

Now you need to add a new function: add two numbers and then add them to 100, handled by class B

class A {
    public int func1(int a, int b) {
        returna - b; }}class B extends A {
    @Override
    public int func1(int a, int b) {
        return a + b;
    }

    public int func2(int a, int b) {
        return func1(a, b) + 100; }}public class LSPClient {
    public static void main(String[] args) {
        B b = new B();
        System.out.println("5-3 =" + b.func1(5.3));
        System.out.println("The 100-10 =" + b.func1(100.10));
        System.out.println("50 + 20 + 100 =" + b.func2(50.20)); }} Output:5-3=8
100-10=110
50+20+100=170
Copy the code

Demeter’s rule

advantages

  1. The coupling degree between classes is reduced and the relative independence of modules is improved
  2. The degree of coupling is reduced, which improves class reusability and system extensibility

disadvantages

  • Overuse of the Demeter principle will result in a large number of mediation classes, resulting in increased system complexity. In the application of Demeter’s rule, repeated trade-offs are needed to ensure high cohesion and low coupling while ensuring clear system structure

implementation

  • The closer the relationship between classes, the greater the degree of coupling, when one class changes, the greater the impact on the other class
  • Solution: Minimize the coupling between classes
  1. Need to emphasize

    • From a dependent’s point of view, only rely on what should be relied on.
    • From the perspective of the dependent, only expose the method that should be exposed.
  2. Need to pay attention to

    • In class partitioning, you should create weakly coupled classes. The weaker the coupling between classes, the better the goal of reuse.
    • In the structure design of the class, the access rights of class members should be reduced as far as possible.
    • In class design, the priority is to make a class immutable.
    • Keep references to other objects to a minimum on references to other classes.
    • Instead of exposing the class’s attribute members, the corresponding accessors (set and GET methods) should be provided.
    • Use Serializable with caution.

Reference:

  • There are six principles of design patterns
  • Dependency Inversion principle — Object-oriented design principle
  • 03. Dependency Inversion Principle (DIP)