Basic principles of Design Patterns

"Design Patterns" Basic Principles of Design Patterns
"Design Patterns" Singleton Patterns "
Design Patterns" Factory Patterns
"Design Patterns" Prototype Patterns "
Design Patterns" Builder Patterns "Design Patterns
" Adapter Patterns "Design
Patterns" Bridge Patterns
"Design Patterns" "Decorator Mode
" Design Mode" Composition Mode
"Design Mode" Appearance Mode "Design Mode"
Flyweight Mode "Design Mode" Proxy Mode "
Design Mode
" Template Method Mode
"Design Mode" Command Mode

The basic principle of the design pattern is the basis of the design pattern design and the basis of the design pattern. Developers need to abide by these basic principles when coding in order to make the written code maintainable, scalable, reusable, and flexible. There are six basic principles: single responsibility principle, interface isolation principle, dependency inversion principle, Richter The principle of substitution, the principle of opening and closing, and the law of Demeter.

1. Single Responsibility Principle

Definition : A class is only responsible for one responsibility , such as class A is responsible for two different responsibilities: responsibility 1 and responsibility 2. When the requirement of responsibility 1 changes and class A is changed, it may cause the execution error of responsibility 2, so the granularity of class A needs to be changed Decomposed into: A1, A2.

Application case :

1. There is a vehicle class Vehicle, which contains instance methods run(), and different vehicles (motorcycles, cars, airplanes, etc.) are "started" by calling run()methods .

public class singleresponsibility {
    
    
    public static void main(String[] args) {
    
    
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        vehicle.run("飞机");
        vehicle.run("轮船");
    }
}

class Vehicle {
    
    
    public void run(String vehicle) {
    
    
        System.out.println(vehicle+"在公路上运行...");
    }
}

In run()the method , the class Vehicle is responsible for multiple responsibilities, namely the operation of motorcycles, cars and airplanes, which violates the single responsibility principle.

2. In order to abide by the principle of single responsibility , the Vehicle class should be responsible for only one responsibility. The Vehicle class can be decomposed into multiple different classes, and run()methods

public class singleresponsibility {
    
    
    public static void main(String[] args) {
    
    
        RoadVehicle roadVehicle = new RoadVehicle();
        roadVehicle.run("摩托车");
        roadVehicle.run("汽车");

        AirVehicle airVehicle = new AirVehicle();
        airVehicle.run("飞机");
		
        WaterVehicle waterVehicle = new WaterVehicle();
        waterVehicle.run("轮船");
    }
}

class RoadVehicle {
    
    
    public void run(String vehicle) {
    
    
        System.out.println(vehicle+"公路运行");
    }
}
class AirVehicle {
    
    
    public void run(String vehicle) {
    
    
        System.out.println(vehicle+"天空运行");
    }
}
class WaterVehicle {
    
    
    public void run(String vehicle) {
    
    
        System.out.println(vehicle+"水中运行");
    }
}

In Case 2, the class is decomposed so that each class is only responsible for one responsibility, which complies with the principle of single responsibility. However, it can be found that after the class is decomposed, not only new classes need to be added, but also the code called by the client needs to be changed.

3. In order to reduce code changes and abide by the single responsibility principle, you can choose to add methods to the class to achieve this goal.

public class singleresponsibility {
    
    
    public static void main(String[] args) {
    
    
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        vehicle.runAir("飞机");
        vehicle.runWater("轮船");
    }
}

class Vehicle {
    
    
    public void run(String vehicle) {
    
    
        System.out.println(vehicle+"在公路上运行...");
    }
    public void runAir(String vehicle) {
    
    
        System.out.println(vehicle+"在天空上运行...");
    }
    public void runWater(String vehicle) {
    
    
        System.out.println(vehicle+"在水中上运行...");
    }
}

Notes on the Single Responsibility Principle :

  • Reduce the complexity of the class, a class is responsible for only one responsibility.
  • Improves readability and maintainability of classes.
  • Reduce the risk of change.
  • Normally, we should abide by the single responsibility principle, but when the logic is very simple, the single responsibility principle can be violated at the code level. The Single Responsibility Principle can only be observed at the method level if the number of methods in the class is sufficiently small.

2. Interface Segregation Principle

Definition : A class's dependence on another class should be established on the smallest interface , and the client should not depend on interfaces it does not need.

Application case :

1. Class A depends on class B through interface Interface1, but only uses methods 1, 2, and 3 of the interface. Class C depends on class D through interface Interface1, but only uses methods 1, 4, and 5 of the interface. Its UML diagram and The implementation code is as follows:

insert image description here

interface Interface1 {
    
    
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}
class B implements Interface1 {
    
    
    @Override
    public void operation1() {
    
    
        System.out.println("B 实现了 operation1");
    }
    @Override
    public void operation2() {
    
    
        System.out.println("B 实现了 operation2");
    }
    @Override
    public void operation3() {
    
    
        System.out.println("B 实现了 operation3");
    }@Override
    public void operation4() {
    
    
        System.out.println("B 实现了 operation4");
    }
    @Override
    public void operation5() {
    
    
        System.out.println("B 实现了 operation5");
    }
}
class D implements Interface1 {
    
    
    @Override
    public void operation1() {
    
    
        System.out.println("D 实现了 operation1");
    }
    @Override
    public void operation2() {
    
    
        System.out.println("D 实现了 operation2");
    }
    @Override
    public void operation3() {
    
    
        System.out.println("D 实现了 operation3");
    }@Override
    public void operation4() {
    
    
        System.out.println("D 实现了 operation4");
    }
    @Override
    public void operation5() {
    
    
        System.out.println("D 实现了 operation5");
    }
}
class A {
    
      //A类通过接口Interface1依赖(使用)B类,但是只会使用到operation1,2,3
    public void depend1(Interface1 i) {
    
    
        i.operation1();
    }
    public void depend2(Interface1 i) {
    
    
        i.operation2();
    }
    public void depend3(Interface1 i) {
    
    
        i.operation3();
    }
}
class C {
    
     //C类通过接口Interface1依赖(使用)D类,但是只会用到1,4,5方法
    public void depend1(Interface1 i) {
    
    
        i.operation1();
    }
    public void depend4(Interface1 i) {
    
    
        i.operation4();
    }
    public void depend5(Interface1 i) {
    
    
        i.operation5();
    }
}

In case 1, class A depends on class B through interface Interface1, class C depends on class D through interface Interface1, and interface Interface1 is not the minimum interface for class A and class C, then class B and class D must implement what they do not need method, which violates the interface segregation principle.

2. In order to prevent class B and class D from implementing their unnecessary methods, the interface Interface1 can be split into three interfaces. Class A and class C respectively establish dependencies with the interfaces they need. The UML diagram and implementation code are as follows Shown:

insert image description here

public class Segregation1 {
    
    
    public static void main(String[] args) {
    
    
        A a = new A();
        a.depend1(new B()); //A类通过接口去依赖(使用)B类
        a.depend2(new B());
        a.depend3(new B());

        C c = new C();
        c.depend1(new D()); //C类通过接口去依赖(使用)D类
        c.depend4(new D());
        c.depend5(new D());
    }
}
//接口
interface Interface1 {
    
    
    void operation1();
}
interface Interface2 {
    
    
    void operation2();
    void operation3();
}
interface Interface3 {
    
    
    void operation4();
    void operation5();
}
class B implements Interface1, Interface2 {
    
    
    @Override
    public void operation1() {
    
    
        System.out.println("B 实现了 operation1");
    }
    @Override
    public void operation2() {
    
    
        System.out.println("B 实现了 operation2");
    }
    @Override
    public void operation3() {
    
    
        System.out.println("B 实现了 operation3");
    }
}
class D implements Interface1, Interface3 {
    
    
    @Override
    public void operation1() {
    
    
        System.out.println("D 实现了 operation1");
    }
    @Override
    public void operation4() {
    
    
        System.out.println("D 实现了 operation4");
    }
    @Override
    public void operation5() {
    
    
        System.out.println("D 实现了 operation5");
    }
}
class A {
    
      //A类通过接口Interface1依赖(使用)B类,但是只会使用到operation1,2,3
    public void depend1(Interface1 i) {
    
    
        i.operation1();
    }
    public void depend2(Interface2 i) {
    
    
        i.operation2();
    }
    public void depend3(Interface2 i) {
    
    
        i.operation3();
    }
}
class C {
    
     //C类通过接口Interface1依赖(使用)D类,但是只会用到1,4,5方法
    public void depend1(Interface1 i) {
    
    
        i.operation1();
    }
    public void depend4(Interface3 i) {
    
    
        i.operation4();
    }
    public void depend5(Interface3 i) {
    
    
        i.operation5();
    }
}

3. Dependency Inversion Principle

Definition :

  • High-level modules should not depend on low-level modules, both should depend on its abstractions.
  • Abstractions should not depend on details, details should depend on abstractions.
  • The core idea of ​​dependency inversion is interface-oriented programming : compared with the variability of details, abstraction is much more stable, and the architecture built on the basis of abstraction is much more stable than the architecture built on the basis of details. In an object-oriented programming language, the abstract here refers to the interface or abstract class, and the details refer to the specific implementation class.
  • The use of interfaces or abstract classes is to formulate specifications without involving any specific operations, and leave the task of showing details to their implementation classes to complete.

Application case :

1. There is a Person class that receives information receive()by .

public class DependencyInversion {
    
    
    public static void main(String[] args) {
    
    
        Person person = new Person();
        person.receive(new Email());
    }
}
class Email {
    
    
    public String getInfo() {
    
    
        return "电子邮件信息:hello,world";
    }
}
class Person {
    
    
    public void receive(Email email) {
    
    
        System.out.println(email.getInfo());
    }
}

In case 1, if the object to be obtained is WeChat, SMS, etc., the class Wechat is added and the corresponding receiving method is also added to Person, which makes the high-level module depend on the underlying module instead of its abstraction.

2. Introduce an abstract interface IReceiverto represent the receiver, Eemailand implement the interface Wechatrespectively IReceiver. After that, Personthe class and the interface IReceiverare dependent, which conforms to the principle of dependency inversion.

public class DependencyInversion {
    
    
    public static void main(String[] args) {
    
    
        //客户端无需改变
        Person person = new Person();
        person.receive(new Email());
        person.receive(new Wechat());
    }
}
interface IReceiver {
    
    
    String getInfo();
}
class Email implements IReceiver{
    
    
    @Override
    public String getInfo() {
    
    
        return "电子邮件信息:hello,world";
    }
}
class Wechat implements IReceiver {
    
    
    @Override
    public String getInfo() {
    
    
        return "微信消息:hello,world";
    }
}
class Person {
    
    
    public void receive(IReceiver receiver) {
    
    
        System.out.println(receiver.getInfo());
    }
}

Three ways of dependency transfer :

1. Interface transfer

public class DependencyPass {
    
    
    public static void main(String[] args) {
    
    
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.open(new HUAWEI());
    }
}
interface IOpenAndClose {
    
    
    public void open(ITV tv);
}
interface ITV {
    
    
    public void play();
}
class HUAWEI implements ITV {
    
    

    @Override
    public void play() {
    
    
        System.out.println("打开华为电视机");
    }
}
class OpenAndClose implements IOpenAndClose {
    
    

    @Override
    public void open(ITV tv) {
    
    
        tv.play();
    }
}

2. Constructor transfer

public class DependencyPass {
    
    
    public static void main(String[] args) {
    
    
        OpenAndClose openAndClose = new OpenAndClose(new HUAWEI());
        openAndClose.open();
    }
}
interface IOpenAndClose {
    
    
    void open();
}
interface ITV {
    
    
    void play();
}
class HUAWEI implements ITV {
    
    

    @Override
    public void play() {
    
    
        System.out.println("打开华为电视机");
    }
}
class OpenAndClose implements IOpenAndClose {
    
    
    
    public ITV tv;
    public OpenAndClose(ITV tv) {
    
    
        this.tv = tv;
    }

    @Override
    public void open() {
    
    
        tv.play();
    }
}

3. Transfer via setter

public class DependencyPass {
    
    
    public static void main(String[] args) {
    
    
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.setTv(new HUAWEI());
        openAndClose.open();
    }
}
interface IOpenAndClose {
    
    
    void open();
    void setTv(ITV tv);
}
interface ITV {
    
    
    void play();
}
class HUAWEI implements ITV {
    
    

    @Override
    public void play() {
    
    
        System.out.println("打开华为电视机");
    }
}
class OpenAndClose implements IOpenAndClose {
    
    

    private ITV tv;

    @Override
    public void open() {
    
    
        tv.play();
    }

    @Override
    public void setTv(ITV tv) {
    
    
        this.tv = tv;
    }
}

Notes on the Dependency Inversion Principle :

  • Low-level modules should have abstract classes or interfaces, or both, so that the stability of the program is better.
  • The variable declaration type should be an abstract class or interface as much as possible, so that there is a buffer between the variable reference and the actual object, which is conducive to program expansion and optimization.

4. Liskov Substitution Principle

Definition : If for every object o1 of type T1, there is object o2 of type T2, so that when all the objects o1 are replaced by o2, the behavior of program P does not change for all programs P defined by T1, then Type T2 is a subtype of type T1. In other words, all places that refer to the base class must be able to use objects of its subclass transparently.

Application case :

1. Class A is the base class, and class B inherits class A and rewrites func()the methods

public class Liskov {
    
    
    public static void main(String[] args) {
    
    
        A a = new A();
        System.out.println("11-3="+a.func(11, 3));
        
        System.out.println("--------------------");

        B b = new B();
        System.out.println("11-3="+b.func(11, 3));//本意是求出11-3,却变成11+3
    }
}
//A类
class A {
    
    
	//增加新的功能,这里重写了A类的方法,可能是无意识的
    public int func(int num1, int num2) {
    
    
        return num1 - num2;
    }
}
class B extends A {
    
    
	//增加新的功能,无意识地重写了A类的方法
    public int func(int a, int b) {
    
    
        return a + b;
    }
}
  • In case 1, class B rewrites func1()the method , even though it may be an unconscious rewrite, the original behavior is to realize the addition of two numbers but it becomes the subtraction of two numbers, which changes the behavior of the program, which violates the Liskov substitution principle.
  • In actual scenarios, it is really simple to achieve new functions by rewriting the methods of the parent class, but it will lead to poor reusability of the entire inheritance system, especially when polymorphism is frequently used.

2. In order to comply with the Liskov substitution principle, the original inheritance relationship can be removed, so that the original parent and child classes inherit a base class, and the inheritance relationship is replaced by dependency, aggregation and composition relationships. The UML diagram and implementation code are as follows:

insert image description here

public class Liskov {
    
    
    public static void main(String[] args) {
    
    
        A a = new A();
        System.out.println("11-3="+a.func1(11, 3));

        System.out.println("--------------------");

        B b = new B();
        System.out.println("11+3="+b.func1(11, 3));//这里本意是求出11+3
        System.out.println("11-3="+b.func2(11, 3)); //11-3
    }
}
//创建一个更加基础的基类
class Base {
    
    
    //把更加基础的方法和成员写到Base类
}
class A extends Base{
    
    
    public int func1(int num1, int num2) {
    
    
        //返回两个数的差
        return num1 - num2;
    }
}
class B extends Base {
    
    
    //如果B类需要使用A类的方法,使用组合关系
    private A a = new A();
   
    public int func1(int a, int b) {
    
    
        return a + b;
    }
    //仍然想使用A的方法
    public int func2(int a, int b) {
    
    
        return a.func1(a,b);
    }
}

Notes on the Liskov Substitution Principle :

  • When using inheritance, follow the Liskov substitution principle, and try not to override the methods of the parent class in the subclass .
  • Inheritance makes the coupling of two classes stronger. In order to reduce the coupling between classes, aggregation, composition and dependency can be used to solve the problem under appropriate circumstances.

5. The principle of opening and closing

Definition :

  • A software entity class, module and function should be open to extension (open to the service provider), closed to modification (to the service consumer), use the abstraction to build the framework and extend the details with the entity.
  • When software requirements change, try to implement changes by extending the behavior of software entities, rather than by modifying existing codes.

Application case :

1. There is a drawing class Graphical, which can drawShape(Shape s)be drawn , s.shapeand different graphics can be drawn according to the passed in. The UML diagram and implementation code are as follows:

insert image description here

public class OCP {
    
    
    public static void main(String[] args) {
    
    
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
    }
}
class GraphicEditor {
    
    
    public void drawShape(Shape s) {
    
    
        if (s.m_type == 1) {
    
    
            drawRectangle(s);
        } else if (s.m_type == 2) {
    
    
            drawCircle(s);
        }
    }

    public void drawRectangle(Shape r) {
    
    
        System.out.println("绘制矩形");
    }

    public void drawCircle(Shape r) {
    
    
        System.out.println("绘制圆形");
    }
}

class Shape {
    
    
    int m_type;
}

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

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

In Case 1, if you need to add a function that can draw triangles, you not only need to add a new class Triangleinheritance Shape, but also modify the class GraphicEditor , add a drawShape(Shape s)new and add a drawTriangle(Shape s)method. This series of behaviors is a modification of the user, which violates the principle of opening and closing.

2. In order to abide by the principle of opening and closing, it can be considered Shapeas abstract class and provide abstract methods draw(), so that subclasses can implement the abstract class. ShapeWhenever there are new graphics types added, they can directly inherit Shapeand implement draw()the method without modifying the user.

public class OCP {
    
    
    public static void main(String[] args) {
    
    
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
        graphicEditor.drawShape(new OtherGraphic());
    }
}

class GraphicEditor {
    
    
    public void drawShape(Shape s) {
    
    
        s.draw();
    }
}
abstract class Shape {
    
    
    public abstract void draw();
}

class Rectangle extends Shape {
    
    
    @Override
    public void draw() {
    
    
        System.out.println("绘制矩形");
    }
}

class Circle extends Shape {
    
    
    @Override
    public void draw() {
    
    
        System.out.println("绘制圆形");
    }
}

class Triangle extends Shape {
    
    
    @Override
    public void draw() {
    
    
        System.out.println("绘制三角形");
    }
}
class OtherGraphic extends Shape {
    
    
    @Override
    public void draw() {
    
    
        System.out.println("绘制其他图形");
    }
}

6. Law of Demeter

Definition :

  • Dimit's law is also called the principle of least knowledge, that is, the less a class knows about the classes it depends on, the better . No matter how complicated the dependent class is, try to encapsulate the logic inside the class and not disclose any information except publicthe method .
  • There is an even simpler definition of Demeter's Law: only communicate with immediate friends.
  • Each object has 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: dependency, association, composition and aggregation, etc.
  • Among them, classes that appear in member variables, method parameters, and method return values ​​are direct friends , while classes that appear in local variables are not friends . Unfamiliar classes are best not to appear inside the class in the form of local variables.

Applications:

1. There is a school with the headquarters and various colleges below, and it is required to print out the IDs of the employees of the school headquarters and the employees of each college.

public class Demeter {
    
    
    public static void main(String[] args) {
    
    
        //创建了一个SchoolManager对象
        SchoolManager schoolManager = new SchoolManager();
        //输出学院员工和学校总部员工ID
        schoolManager.printAllEmployee(new CollegeManager());
    }
}
//学校总部员工
class Employee {
    
    
    private String id;

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getId() {
    
    
        return id;
    }
}
class CollegeEmployee {
    
    
    private String id;
    
    public void setId(String id) {
    
    
        this.id = id;
    }
    
    public String getId() {
    
    
        return id;
    }
}
class CollegeManager {
    
    
    //返回学院所有员工
    public List<CollegeEmployee> getAllEmployee() {
    
    
        ArrayList<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工id="+i);
            list.add(emp);
        }
        return list;
    }
}

class SchoolManager {
    
    
    //返回学校总部所有员工
    public List<Employee> getAllEmployee() {
    
    
        ArrayList<Employee> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
    
    
            Employee emp = new Employee();
            emp.setId("学校总部员工id="+i);
            list.add(emp);
        }
        return list;
    }
    //输出学校总部和学院员工信息
    public void printAllEmployee(CollegeManager sub) {
    
     //CollegeManager作为printAllEmployee局部变量
        List<CollegeEmployee> list1 = sub.getAllEmployee();
        System.out.println("--------------分公司员工-------------");
        for (CollegeEmployee e : list1) {
    
    
            System.out.println(e.getId());
        }
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("-------------学校总部员工-------------");
        for (Employee e : list2) {
    
    
            System.out.println(e.getId());
        }
    }
}

In case 1, CollegeEmployeeit appears in SchoolManager the class SchoolManagerof a local variable, as a strange class of , not its direct friend, which violates Dimit's law.

2. In order to abide by Dimit's law, the coupling of this non-direct friend relationship should be avoided in the class, and printEmployee()the method encapsulated CollegeManagerin the class, and the class should CollegeManagerbe used as SchoolManagera direct friend of the class.

public class Demeter1 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("使用迪米特法则的改进");
        //创建了一个SchoolManager对象
        SchoolManager schoolManager = new SchoolManager();
        //输出学院员工id 和 学校总部员工信息
        schoolManager.printAllEmployee(new CollegeManager());
    }
}

//学校总部员工
class Employee {
    
    
    private String id;

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getId() {
    
    
        return id;
    }
}

class CollegeEmployee {
    
    
    private String id;
    
    public void setId(String id) {
    
    
        this.id = id;
    }
    public String getId() {
    
    
        return id;
    }
}

class CollegeManager {
    
    
    //返回学院所有员工
    public List<CollegeEmployee> getAllEmployee() {
    
    
        ArrayList<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工id="+i);
            list.add(emp);
        }
        return list;
    }
    public void printEmployee() {
    
    
        List<CollegeEmployee> list1 = this.getAllEmployee();
        System.out.println("--------------分公司员工-------------");
        for (CollegeEmployee e : list1) {
    
    
            System.out.println(e.getId());
        }
    }
}

class SchoolManager {
    
    
    //返回学校总部所有员工
    public List<Employee> getAllEmployee() {
    
    
        ArrayList<Employee> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
    
    
            Employee emp = new Employee();
            emp.setId("学校总部员工id="+i);
            list.add(emp);
        }
        return list;
    }
    //输出学校总部和学院员工信息
    public void printAllEmployee(CollegeManager sub) {
    
    
        //将获取学院员工的方法封装到CollegeManager类里面
        sub.printEmployee();
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("-------------学校总部员工-------------");
        for (Employee e : list2) {
    
    
            System.out.println(e.getId());
        }
    }
}

Precautions for Demeter's Law :

  • The core idea is to reduce the coupling between classes.
  • To reduce unnecessary dependencies for each class and reduce the coupling relationship between classes is not strictly required to have no dependencies at all.

Guess you like

Origin blog.csdn.net/weixin_43252521/article/details/127187936