Seven principles of design mode

Keywords: Programming Java Apache less

In the process of writing our call later, we will face from coupling. Cohesion and maintainability, scalability, reusability, flexibility and other challenges, design patterns to make the program have better:

  • Code reusability (i.e. code with the same function, not written many times)
  • Readability (i.e. programming specification, easy for others to read and understand)
  • Reliability (i.e. when we add new functions, it is very convenient and has no impact on the original functions)
  • Make the program realize the characteristics of high cohesion and low coupling

Seven principles of design pattern

Don't ask why design patterns should be designed like this. This is just a development specification of design patterns. It doesn't matter if you don't follow it, but we should follow this specification for your convenience.

Principle of single responsibility

Basic introduction

The simple understanding is: a class is only responsible for one responsibility, just like the author, only enough to love one person in life, although still single at present.

Attention points

  • Reduce the complexity of classes, one class is responsible for only one responsibility
  • Improve the readability and maintainability of the class
  • Reduce risks caused by changes
  • Only when the logic is simple enough, can we violate the single responsibility principle at the code level; only when there are only enough methods in the class, can we keep the single principle at the method level

Interface isolation principle

Basic introduction

The simple understanding is that the dependence of one class on another should be based on the smallest interface. For example, I am from Anhui. Anhui is a province of China. I am dependent on China. I am dependent on Anhui. But at this time, although it can be said that I am dependent on China, we can't say that because Anhui is the least dependent interface in our dependency relationship. So, we are dependent on Anhui (roughly that means). Let's have a look Figure:

A will depend on class B through the interface, and C will depend on D through the interface. If the interface is not the minimum interface for a and C, then B and D will implement the methods they do not need;

According to the isolation principle, a and C establish the dependency relationship with the interface they need, that is, adopt dependency isolation.

Existing problems and improvement ideas

1) Class A depends on class B through the interface, and class C depends on D through the interface. If the interface is not the smallest interface for AC, BD must implement the methods they do not need;

2) The interface is divided into several independent interfaces. AC establishes a dependency relationship with the interface they need, that is, adopts dependency isolation.

3) The renderings are as follows

Principle of Dependence Inversion

Basic introduction

The principle of dependency reversal refers to:

  • The high-level pattern should not depend on the low-level pattern, both of which should depend on its abstraction.
  • Abstraction should not depend on details. Details should depend on abstraction
  • The central idea of dependency inversion is interface oriented programming
  • The principle of dependency inversion is based on the idea that abstract things are much more stable than the variability of details. The architecture based on abstraction is more stable than that based on details. In Java, abstraction refers to an interface or an abstract class, and details are concrete implementation classes.
  • The purpose of using an interface or abstract class is to specify a specification without any specific operation. The task of presenting details is assigned to their implementation class.

Here's an example:

package com.ci123.dependence;

/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p> Principle of Dependence Inversion
 * Project: design-pattern
 * Package: com.ci123.dependence
 * Version: 1.0
 * <p>
 * Created by SunYang on 2019/11/7 10:14
 */
public class DependenceInversionPrinciple {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new EmailIR());
        person.receive(new WeiXinIR());
    }
}

class Email {
    public String getInfo() {
        return "Email information: Hello,World";
    }
}

/**
 * Complete the function of Person receiving messages
 * Mode 1 Analysis:
 * 1. Simple, easy to think of
 * 2. If the objects we get are wechat, SMS, etc., we need to add new classes, and at the same time, we need to add corresponding receiving methods for Person
 * 3. Solution: introduce an abstract interface IReceiver to represent the receiver, so that the Person class depends on the interface IReceiver
 * Because Email,WeiXin, etc. belong to the receiving range, it's OK for them to implement IReceiver interface respectively, so we conform to the dependency inversion principle
 */
class Person {
    public void receive(IRceiver iRceiver) {
        System.out.println(iRceiver.getInfo());
    }
}

interface IRceiver {
    String getInfo();
}

class EmailIR implements IRceiver {

    @Override
    public String getInfo() {
        return "Email information(IReceiver): Hello,World";
    }
}

class WeiXinIR implements IRceiver {

    @Override
    public String getInfo() {
        return "WeChat info(IReceiver): Hello,World";
    }
}

Three kinds of transmission of dependency

  • Interface transfer
  • Construction method transfer
  • setter delivery

Attention points

  • Low level modules should have abstract classes or interfaces as much as possible, or both, so the program stability is better
  • The declaration type of variable should be abstract class or interface as far as possible, so that there is a buffer between our variable reference and the actual object, which is conducive to program expansion and optimization
  • Integrated follow the principle of Richter's replacement

Richter's principle of substitution

Thinking and explanation of inheritance in OO

  • Inheritance contains the meaning that all implemented methods in the parent class actually set specifications and contracts. Although it does not force all the subclasses to abide by these contracts, if the subclass modifies the implemented methods arbitrarily, it will damage the entire integration system
  • Inheritance not only brings convenience to program design, but also brings disadvantages. For example, the use of inheritance will bring intrusion to the program, reduce the portability of the program, and increase the coupling between objects. If a class is inherited by other classes, when the class needs to be modified, all the subclasses must be considered. After the parent class is modified, all the functions related to the subclass will be considered There is a possibility of failure.
  • Thinking: how to use inheritance correctly in programming? =>Richter's principle of substitution

Basic introduction

  1. If there is object o2 of type T2 for each object o1 of type T1, so that when all objects o1 defined by T1 are replaced by o2, the behavior of program P does not change, then type T2 is a subtype of type T1. In other words, all references to the base class must use its subclass objects transparently.
  2. When using inheritance, follow the principle of leech substitution, and try not to override the methods of the parent class in the child class
  3. The Riemannian substitution principle tells us that inheritance actually enhances the coupling of the two subclasses. When appropriate, problems can be solved through aggregation, combination, and dependency.

Here's an example:

package com.ci123.base.liskov;
/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p>
 * Project: design-pattern
 * Package: com.ci123.base.liskov
 * Version: 1.0
 * <p> As you can see here, our B rewrites the func1(int num1,int num2) method of A, unintentionally changing the subtraction to the addition, but our entire inheritance system
 * It has been destroyed, which will lead to poor reusability of inheritance system, especially when running polymorphism is tedious
 * <p>
 * Improvement:
 * The general approach is: the original parent class and child class inherit a more popular base class. The original inheritance relationship is removed and replaced by dependency, aggregation, combination and other relationships.
 * Namely:
 * base <- A
 * base <- B
 * B <<- A
 * <p>
 * Created by SunYang on 2019/11/7 10:55
 */
public class Liskov {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("5-3="   a.func1(5, 3));
        B b = new B();
        System.out.println("5-3="   b.func1(5, 3)); // This is supposed to output 5-3
        System.out.println("/************************************************/");
        AA aa = new AA();
        System.out.println("5-3="   aa.func1(5, 3));
        BB bb = new BB();
        System.out.println("5-3="   bb.func(5, 3)); // This is supposed to output 5-3
    }
}
class Base {
    // There are very basic methods and members here
}
/********************************************************************************************/
class A {
    // Returns the difference between two numbers
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}
// B inherit A
// Add a new function to add two numbers
class B extends A {
    // Class A method is rewritten here
    @Override
    public int func1(int num1, int num2) {
        // Here is the unconscious rewriting, the rewriting we don't need, which we meant to require subtraction
        return num1   num2;
    }

    public int func2(int num1, int num2) {
        return func1(num1, num2) * 8;
    }
}
/********************************************************************************************/
class AA {

    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}
class BB extends Base {
    // Class A method is rewritten here
    public int func1(int num1, int num2) {
        // Here is the unconscious rewriting, the rewriting we don't need, which we meant to require subtraction
        return num1   num2;
    }

    public int func2(int num1, int num2) {
        return func1(num1, num2) * 8;
    }

    private AA aa = new AA();
    // I want to use AA method here
    public int func(int num1 , int num2){
        return this.aa.func1(num1 , num2) ;
    }
}

Opening and closing principle

Basic introduction

  • Is the most basic and important design principle in programming
  • A software entity such as a class, module kernel function should be open to extension (to provider) and closed to modification (to user). Build framework with abstraction and extend details with implementation
  • When the software needs to change, try to realize the change by expanding the behavior of the software entity instead of modifying the existing code
  • Follow other principles in programming, and the purpose of using design mode is to follow the opening and closing principles
package com.ci123.base.ocp;

/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p>
 * Project: design-pattern
 * Package: com.ci123.base.ocp
 * Version: 1.0
 * <p> 1. The advantages are easy to understand and operate
 * 2. The disadvantage is that it violates the OCP principle of design pattern, that is, it is open to extension (provider) and closed to modification (user). That is, when we add new functions to a class, try not to modify the code, or try to modify the code as little as possible
 * 3. If we want to add a new type of graphics, we have to make a lot of changes
 * Created by SunYang on 2019/11/7 11:20
 */
public class OCPDemo {
    public static void main(String[] args) {
        // Use to see the problems
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
    }
}
// User, drawing
class GraphicEditor{
    public void drawShape(Shape shape){
        switch (shape.m_type){
            case 1:
                drawRectangle(shape);
                break;
            case 2:
                drawCircle(shape);
                break;
            case 3:
                drawTriangle(shape);
                break;
            default:
                break;
        }
    }

    private void drawRectangle(Shape shape){
        System.out.println("rectangle");
    }
    private void drawCircle(Shape shape){
        System.out.println("circular");
    }
    private void drawTriangle(Shape shape){
        System.out.println("Triangle");
    }
}
// Base class
class Shape{
    int m_type ;
}
class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1 ;
    }
}
class Circle extends Shape{
    Circle(){
        super.m_type = 2 ;
    }
}
// Add triangle
class Triangle extends Shape{
    Triangle(){
        super.m_type = 3 ;
    }
}

Take a look at the modified:

public class OCPDemo {
    public static void main(String[] args) {
        // Use to see the problems
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
        graphicEditor.drawShape(new Other());
    }
}

// User, drawing
class GraphicEditor {
    public void drawShape(Shape shape) {

        shape.draw();
    }

}

// Base class
abstract class Shape {
    int m_type;

    abstract void draw();
}

class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }

    @Override
    void draw() {
        System.out.println("rectangle");
    }
}

class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }

    @Override
    void draw() {
        System.out.println("circular");
    }
}

// Add triangle
class Triangle extends Shape {
    Triangle() {
        super.m_type = 3;
    }

    @Override
    void draw() {
        System.out.println("Triangle");
    }
}

class Other extends Shape {
    Other() {
        super.m_type = 4;
    }

    @Override
    void draw() {
        System.out.println("Other");
    }
}

Dimiter's law

Basic introduction

  • One object should have the least knowledge of other objects
  • Class is closely related to class, with high coupling
  • Dimiter's law is also known as the best known principle, that is, the less a class knows about its own dependent classes, the better. In other words, no matter how complex the dependent class is, try to encapsulate the logic inside the class. In addition to the public method provided, it does not disclose any information;
  • Dimiter's law also has a simpler definition: only communicate with direct friends
  • Direct friend: every object has a coupling relationship with other objects. As long as there is a coupling relationship between two objects, we say that these two objects have a friend relationship with many ways of coupling, and the classes appearing in local variables are not direct friends. That is to say, it is better not to use local variable shape to appear inside a class.
package com.ci123.base.dp;

import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xpath.internal.SourceTree;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;

/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p> Dimiter's law
 * Project: design-pattern
 * Package: com.ci123.base.dp
 * Version: 1.0
 * <p>
 * Created by SunYang on 2019/11/7 11:50
 */
// There is a school with subordinate schools and headquarters. Now it is required to print out the employee ID of the school headquarters and the employee ID of the school
public class DemeterDemo {
    public static void main(String[] args) {
        // Create a SchoolManager object first
        SchoolManager schoolManager = new SchoolManager();

        // Output employee ID of college and employee information of school headquarters
        schoolManager.printAllEmployee(new CollegeManager());
    }
}

// Staff of school headquarters
class Employee{
    private String id ;
    public void setId(String id){
        this.id = id ;
    }
    public String getId(){
        return id ;
    }
}
// College staff
class CollegeEmployee{
    private String id ;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
// Management of the staff of the school of management
class CollegeManager{
    // All employees returning to the College
    public List<CollegeEmployee> geAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i  ) {
            CollegeEmployee employee = new CollegeEmployee();
            employee.setId("College staff ID="   i);
            list.add(employee) ;
        }
        return list ;
    }
}
// Analyze the direct friends of SchoolManager class, such as employee and collegemanager
// The CollegeEmployee is not a direct friend relationship, but a strange class, which violates Dimitar's law
class SchoolManager{
    // Total staff returning to school
    public List<Employee> getAllEmployees(){
        List<Employee> list = new ArrayList<>();

        for (int i = 0; i < 20; i  ) {
            Employee employee = new Employee();
            employee.setId("School ID="   i);
            list.add(employee) ;
        }
        return list ;
    }
    // This method can output the information ID of school headquarters and college employees
    void printAllEmployee(CollegeManager manager){
        // Analysis problem
        // 1. The CollegeEmployee here is not a direct friend of SchoolManager
        // 2. The collegeemployee appears in the SchoolManager as a local variable
        // 3. Violation of Dimitar's law

        // Access to college staff
        List<CollegeEmployee> list = manager.geAllEmployee();
        System.out.println("========== College staff ============");
        for (CollegeEmployee employee : list) {
            System.out.println(employee.getId());
        }

        // Access to school staff
        List<Employee> allEmployee = this.getAllEmployees();
        System.out.println("========== School staff ============");
        for (Employee employee : allEmployee) {
            System.out.println(employee.getId());
        }
    }
}

The improvements are as follows:

// Management of the staff of the school of management
class CollegeManager{
    // All employees returning to the College
    public List<CollegeEmployee> geAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i  ) {
            CollegeEmployee employee = new CollegeEmployee();
            employee.setId("College staff ID="   i);
            list.add(employee) ;
        }
        return list ;
    }
    public void printEmployee(){
        // Access to college staff
        List<CollegeEmployee> list = this.geAllEmployee();
        System.out.println("========== College staff ============");
        for (CollegeEmployee employee : list) {
            System.out.println(employee.getId());
        }
    }
}

class SchoolManager{
    // Total staff returning to school
    public List<Employee> getAllEmployees(){
        List<Employee> list = new ArrayList<>();

        for (int i = 0; i < 20; i  ) {
            Employee employee = new Employee();
            employee.setId("School ID="   i);
            list.add(employee) ;
        }
        return list ;
    }
    // This method can output the information ID of school headquarters and college employees
    void printAllEmployee(CollegeManager manager){
        // Encapsulated in the college Manager
        manager.printEmployee();

        // Access to school staff
        List<Employee> allEmployee = this.getAllEmployees();
        System.out.println("========== School staff ============");
        for (Employee employee : allEmployee) {
            System.out.println(employee.getId());
        }
    }
}

Attention points

  • The core of Dimiter's law is to reduce the coupling between classes
  • Note, however, that since each class reduces unnecessary dependencies, Dimitri's law only requires that the coupling relationship between classes (objects) be reduced, not that there is no dependency at all

Principle of composite reuse

Basic introduction

Bottom line: try to use composition / aggregation instead of inheritance

Core idea of design principle

  • Find out the possible changes in the application, separate them, and don't mix them with the code that doesn't need to change
  • Programming for interfaces, not for implementation
  • Strive for loose coupling design between interactive objects
88 original articles published, 20 praised, 10000 visitors+
Private letter follow

Posted by AwptiK on Wed, 15 Jan 2020 03:46:08 -0800