Software Design - Seven Principles of Object Orientation

Preface

        Software design patterns and design principles are the basic skills for becoming a software architect. A good understanding of these basic knowledge is undoubtedly very important. In this article, Lizhi will sort out the seven principles of object-oriented in more detail. It would be better if you read this part first and then learn the design pattern hahahaha~~~


        In software development, in order to improve the maintainability and reusability of software systems, increase the scalability and flexibility of software, thereby improving software development efficiency and saving software development and maintenance costs, development must be based on object-oriented Seven principles to implement. First we need to understand some terminology:

  • Maintainability: Code can be quickly modified or added without destroying the original code design or introducing new bugs;
  • Scalability: New functional codes can be added through expansion without modifying or slightly modifying the original code;
  • Reusability: Minimize the writing of duplicate code and directly reuse existing code;
  • Cohesion: the closeness of the internal elements of the module. The higher the cohesion, the better the module independence, maintainability, and reusability;
  • Coupling: the relationship between modules. The higher the coupling, the more complex the relationship between modules, and the worse the maintainability and reusability;

Seven fundamental aspects of object-oriented programming:

Single Responsibility Principle SRP

        A class or module is only responsible for completing one function. In layman's terms, if this class contains two or more business-independent functions, then the responsibilities of this class are not single enough, and it should be split into multiple classes with more single functions and finer granularity. The single responsibility principle is a guideline for achieving high cohesion and low coupling . It is the simplest but most difficult principle to apply. It requires designers to discover and separate the different responsibilities of a class.

The responsibilities of the two classes in the demo below are divided

package com.crj.principle;

import java.util.List;

public class UserInfo {
    private Long UserID;
    private String userName;
    private String phone;
    private List<Address> addresses;

    public void save(){
        System.out.println("用户信息");
    }
}
package com.crj.principle;

public class Address {
    private String province;
    private String city;
    public void saveAddress(){
        System.out.println("保存地址信息");
    }
}

Richter Substitution Principle LSP

        Subclass objects can replace parent class objects anywhere in the program, and ensure that the logical behavior of the original program remains unchanged and the correctness is not destroyed. Popular understanding: Subclasses can extend the functions of the parent class, but they cannot change the original functions of the parent class. In other words, when a subclass inherits a parent class, try not to override the parent class's methods except adding new methods to complete new functions. If you override the parent class's methods, errors may occur during program operation. If polymorphism must be used, the parent class can be designed as an abstract parent class or interface . All places where parent classes can be used must be able to use subclasses transparently.

Let’s take a look at an example: 

package com.crj.principle.t12;

public class Bird {
    protected double runspeed;
    protected double flyspeed;

    public void setRunspeed(double runspeed) {
        this.runspeed = runspeed;
    }

    public void setFlyspeed(double flyspeed) {
        this.flyspeed = flyspeed;
    }
    public double canGetInstance(double distance){
        return distance/flyspeed;
    }
}

        We have defined a Bird class. If subclass A overrides Bird's setFlyspeed method and modifies its function (for example, for a flightless bird, we return 0.0), then the Liskov substitution principle is violated. At this time, we can abstract a parent class on Bird and let the new flightless bird C inherit the corresponding parent class to follow the Liskov substitution principle.


Open and Close Principle OCP

        Open for extension, closed for modification . When the program needs to be modified and expanded, the original code cannot be modified to achieve a hot-swappable effect. To achieve this effect, we need to use interfaces and abstract classes. Because abstraction has good flexibility and wide adaptability, as long as the abstraction is reasonable, the stability of the software architecture can be basically maintained. The volatile details in the software can be extended from the implementation class derived from the abstraction. When the software needs change, you only need to re-derive an implementation class according to the requirements to extend it.

Dependency Inversion Principle DIP

        Modules must rely on abstraction, not implementation classes, and should be programmed for interfaces , not for implementation. High-level modules should not directly depend on low-level modules, thus reducing the coupling between the client and the implementation module. To put it simply, when we encapsulate a module, we should first define the corresponding functional methods in the interface or abstract class, and then use the implementation class to implement the corresponding specific functions. Instead of directly using one class to implement all the functions, when the specific functions are implemented, The class will only call the implementation method of the corresponding interface object.

Does the above paragraph seem difficult to understand? Let’s take a look at this example: Assume a scenario where we need to select different CPUs and memories to assemble a computer. Of course, we can only use classes to implement functions, but the CPU and memory in the class The type is hard-coded. If there are new choices, it will undoubtedly make the code more difficult to maintain. Therefore, we can implement the function based on the dependency inversion principle:

  •  First define two interfaces

  •  Then define their respective interface implementation classes

  • Define object class
package com.crj.principle.t3;

import com.crj.principle.t3.cpu.CPU;
import com.crj.principle.t3.memory.Memory;

public class Computer {
    private CPU cpu;
    private Memory memory;

    public Computer(CPU cpu, Memory memory) {
        this.cpu = cpu;
        this.memory = memory;
    }
    public Computer(){}

    public void startRun(){
        cpu.calculate();
        memory.storage();
    }
}
  • test type test 
package com.crj.principle.t3;

import com.crj.principle.t3.cpu.AmdCPU;
import com.crj.principle.t3.cpu.CPU;
import com.crj.principle.t3.memory.IMemory;
import com.crj.principle.t3.memory.Memory;

public class Test {
    public static void main(String[] args) {
        CPU cpu = new AmdCPU();
        Memory memory = new IMemory();
        Computer com = new Computer(cpu,memory);
        com.startRun();
    }
}

It mainly depends on the Computer class. When we execute the startRun() method, we do not need to know the type of the specific product (CPU, AMD). Instead, we can call the type method of its interface class to achieve it. This reduces the coupling of the system and complies with both the dependency inversion principle and the opening and closing principle.


Interface isolation principleISP

        Interface Segregation Principle, the minimum granularity design of the interface. A client should not be forced to rely on methods it does not use; one class's dependencies on another class should be based on the smallest interface. For a class to implement an interface, it must implement all abstract methods of this interface. If the interface is designed to be too large, the implementation class will be forced to implement unnecessary abstract methods. To put it simply, when defining an interface, you should consider that the implementation class should not be forced to implement unnecessary methods in the interface ! The solution is to design the interface according to the smallest granularity of the interface function. 


Demeter Principle LoD

        Law of Demeter, the Law of Demeter comes from a research project called Demeterl at Northeastern University in the United States in 1987. Only contact friends and do not talk to "strangers". If two software entities do not need to communicate directly, then direct mutual calls should not occur and the calls can be forwarded through a third party. Its purpose is to reduce the coupling between classes and improve the relative independence of modules.  


Synthetic Reuse Principle CRP

        Composite Reuse Principle, the principle of composite reuse means: try to use association relationships such as combination or aggregation to implement it first, and then consider using inheritance relationships to implement it. Generally, class reuse is divided into two types: inheritance reuse and synthetic reuse. Although inheritance reuse has the advantages of simplicity and ease of implementation, it also has the following disadvantages:

  • Inheritance reuse destroys the encapsulation of classes. Because inheritance exposes the implementation details of the parent class to the child class, and the parent class is transparent to the child class, this kind of reuse is also called "white box" reuse.
  • The coupling between subclasses and parent classes is high. Any change in the implementation of the parent class will lead to changes in the implementation of the subclass, which is not conducive to the expansion and maintenance of the class.

When using combination or aggregation reuse, existing objects can be incorporated into new objects and become part of the new object. The new object can call the functions of the existing objects. The advantages are as follows:

  • It maintains the encapsulation of classes. Because the internal details of the component objects are invisible to the new object, this type of reuse is also called "black box" reuse.
  • The coupling between objects is low. An abstract abstract class or interface can be declared as a member of a class).

Summarize

        The sorting is completed, and the knowledge about software design is sorted out here. Next, Lizhi will continue to study middleware in depth. You can take time to come back and read the notes later. Knowledge needs to be reviewed, summarized and shared ~ I hope it can help friends in need hehe.

Today has become the past, but we still look forward to the future tomorrow! I am Litchi, and I will accompany you on the road of technological growth~~~

If the blog post is helpful to you, you can give Lizhi three clicks. Your support and encouragement are Lizhi’s biggest motivation!

If the content of the blog post is incorrect, you are also welcome to criticize and correct it in the comment area below! ! !

Guess you like

Origin blog.csdn.net/qq_62706049/article/details/132796528