Seven Design Principles in Java

1. Open and close principle

Open for extension, closed for modification. When the program needs to be expanded, the original code cannot be modified, and a hot-swap effect must be achieved. In short, it is to make the program scalable and easy to maintain and upgrade.

The following is an example of setting a skin for an input method:

// 抽象皮肤接口
public interface Skin {
    // 显示的方法
    void display();
}
// 默认皮肤类
public class DefaultSkin implements Skin {
    @Override
    public void display() {
        System.out.println("默认皮肤");
    }
}
// 下班了皮肤
public class OffWorkSkin implements Skin{
    @Override
    public void display() {
        System.out.println("下班了皮肤");
    }
}
// 输入法类
@Data
public class Input {
    // 设置皮肤
    private Skin skin;
    public void display() {
        skin.display();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建输入法对象
        Input input = new Input();
        // 创建皮肤对象
        Skin defaultSkin = new DefaultSkin();
        // 设置默认皮肤
        input.setSkin(defaultSkin);
        // 显示皮肤
        input.display(); // 默认皮肤
        // 换成下班了皮肤
        Skin offWorkSkin = new OffWorkSkin();
        input.setSkin(offWorkSkin);
        input.display();// 下班了皮肤
    }

2. Single Responsibility Principle

As far as a class is concerned, there should be only one cause of change. There should be only one responsibility.

To put it bluntly, a class or a method should do one thing.

The following is an example of modifying a user:

public class UpdateUser {
    // 不符合单一职责原则
    public void updateUser(String type, String oldPassword, String newPassword, String oldUserName, String newUserName) {
        if ("修改密码".equals(type)) {
            System.out.println(oldPassword + "修改密码为:" + newPassword);
        } else if ("修改账号".equals(type)) {
            System.out.println(oldUserName + "修改账号" + newUserName);
        }
    }
    // 符合单一职责原则
    public void updatePassword(String oldPassword, String newPassword) {
        System.out.println(oldPassword + "修改密码为:" + newPassword);
    }
    public void updateUserName(String oldUserName, String newUserName) {
        System.out.println(oldUserName + "修改账号" + newUserName);
    }
}

It can be seen that the above is distinguished according to the type of operation. Different types execute different logics, and the two things of changing the account and changing the password are coupled together. If the client passes the wrong type during the operation, then it will happen. mistake;

The latter separates the logic of modifying the account and modifying the password, and each performs its own duties without interfering with each other. The functions are clear and clear, in line with the principle of single responsibility.

3. Dependency Inversion Principle

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. Simply put, it is required to program the abstraction, not the implementation, which reduces the coupling between the client and the implementation module.

The following is an example of assembling a computer (for a simple example, only the CPU is reserved for the computer, hehehe):

// Intel处理器类
public class IntelCpu {
    public void getCpu() {
        System.out.println("使用Intel处理器");
    }
}
// 电脑
@Data
public class Computer {
    private IntelCpu intelCpu; // CPU
    public void run() {
        intelCpu.getCpu();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建电脑对象
        Computer computer = new Computer();
        // 设置CPU
        computer.setIntelCpu(new IntelCpu());
        computer.run(); // 使用Intel处理器
    }

It can be seen that the computer assembled above can only use Intel CPU, if I want to change to AMD CPU, it will not work. This is a violation of the Dependency Inversion Principle. The modified code is as follows:

// CPU class 
public interface Cpu { 
    void getCpu(); 
} 
// Intel processor class 
public class IntelCpu implements Cpu { 
    @Override 
    public void getCpu() { 
        System.out.println("Use Intel processor"); 
    } 
} 
// Amd processor class 
public class AmdCpu implements Cpu { 
    @Override 
    public void getCpu() { 
        System.out.println("Use Amd processor"); 
    } 
} 
// Computer 
@Data 
public class Computer { 
    private Cpu cpu; 
    public void run() { 
        cpu.getCpu(); 
    } 
} 
    // test 
    public static void main(String[] args){ 
        // create computer object
        Computer computer = new Computer(); 
        // Set CPU to Intel processor 
        computer.setCpu(new IntelCpu()); 
        computer.run(); // Use Intel processor 
        // Set CPU to Amd processor 
        computer.setCpu( new AmdCpu()); 
        computer.run(); // use Amd processor 
    }

At this time, when the user needs to replace the CPU, he only needs to create an implementation class to realize the CPU interface without modifying the original interface code, which conforms to the principle of opening and closing.

4. Interface Segregation Principle

A client should not be forced to depend on methods it doesn't use; dependencies of one class on another should be based on the smallest possible interface.

Here's an example with a security gate:

Now there is a brand A safety door with fire prevention and anti-theft function, so the function of fire prevention and anti-theft is extracted into an interface, namely:

// 安全门接口
public interface Door {
    //防火
    void fireproof();
    //防水
    void waterproof();
}
// 安全门接口
public interface Door {
    //防火
    void fireproof();
    //防水
    void waterproof();
}

So what if we now have a safety door of the B brand, which only has waterproof function? So it is obviously impossible to directly implement the interface of the security door, so how should it be improved? The improvements are as follows:

// 防火功能
public interface FireProof {
    void fireProof();
}
// 防水功能
public interface WaterProof {
    void waterProof();
}
// A类门,具有防火防水功能
public class ADoor implements WaterProof, FireProof {
    @Override
    public void fireProof() {
        System.out.println("A品牌安全门防火功能");
    }
    @Override
    public void waterProof() {
        System.out.println("A品牌安全门防水功能");
    }
}// B类安全门
public class BDoor implements WaterProof {
    @Override
    public void waterProof() {
        System.out.println("B品牌安全门防水功能");
    }
}

After this improvement, it can be seen that when security doors of different brands have different functions, the interface of which function can be realized if there is any function. An anti-theft interface, and then the C-type door can realize the fire-proof, waterproof and anti-theft interface. This is the Interface Segregation Principle.

5. Law of Demeter

Demeter's law is also called the principle of least knowledge. If two software entities do not need to communicate directly, then no direct mutual call should occur, and the call can be forwarded through a third party. Its purpose is to reduce the degree of coupling between classes and improve the relative independence of modules.

The current object itself, the member objects of the current object, the objects created by the current object, the method parameters of the current object, etc. These objects are associated, aggregated or combined with the current object, and the methods of these objects can be directly accessed.

Here is an example of a star, fan, agency:

// 明星类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star {
    private String name;
}
// 粉丝类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fans {
    private String name;
}
// 经纪公司类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
    private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Agent {
    private Star star;
    private Fans fans;
    private Company company;
    //和粉丝见面的方法
    public void meeting(){
        System.out.println(star.getName() + "和粉丝"+fans.getName() + "见面");
    }
    //和媒体公司洽谈的方法
    public void business(){
        System.out.println(star.getName() + "和" + company.getName() + "洽谈");
    }
}
    // 测试
    public static void main(String[] args) {
        //创建明星对象
        Star star = new Star("严晓波");
        //创建粉丝对象
        Fans fans = new Fans("彭晓锋");
        //创建公司对象
        Company company = new Company("杨永信电疗娱乐有限公司");
        //创建经纪人对象
        Agent agent = new Agent(star, fans, company);
        agent.meeting();// 严晓波和粉丝彭晓锋见面
        agent.business();// 严晓波和杨永信电疗娱乐有限公司洽谈
    }

The daily affairs of the star here are handled by the agent, such as meeting with fans and negotiating with media companies. The friend of the star here is the agent, but the fans and the company are strangers, so Dimit’s law is suitable. This example shows that Dimiter's Law is mainly to reduce the coupling between celebrities, fans and companies.

6. Liskov Substitution Principle

Wherever a base class can appear, a subclass must appear. Subclasses can extend the functions of the parent class, but cannot change the original performance of the parent class. In other words, when a subclass inherits from a parent class, try not to override the methods of the parent class except for adding new methods to complete new functions.

Here are examples of squares that are not rectangles:

// 长方形
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Rectangle {
    private Double length;
    private Double width;
}
// 正方形类(错误继承长方形)
public class Square extends Rectangle {
    @Override
    public void setLength(Double length) {
        super.setLength(length);
        super.setWidth(length);
    }
    public void setWidth(Double width) {
        super.setLength(width);
        super.setWidth(width);
    }
}

// 测试类
public class Test {
    public static void main(String[] args){
        // 创建长方形对象
        Rectangle rectangle = new Rectangle(15.0, 10.0);
        // 扩宽
        resize(rectangle);
        // 15.0
        // 16.0
        print(rectangle);
        System.out.println("====================");
        Square square = new Square();
        square.setLength(10.0);
        // 扩宽
        resize(square);
        // 死循环 直到oom
        print(square);
    }
    // 扩宽修正方法
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }
    // 打印长宽
    public static void print(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }
}

You can see from running the above code that when we call the resize() method, ordinary rectangles can work normally; but when the object calling the resize() method is a square, an infinite loop will occur, because the squares have equal length and width. The conditions for widening are never met. So it can be concluded that in the resize() method, the parameters of the rectangle cannot be replaced by the parameters of the square. If the replacement is performed, the expected effect will not be obtained, so the inheritance relationship between the Square and Rectangle classes violates According to the Liskov substitution principle, the inheritance relationship between them is not established. The modified code is as follows:

// 四边形接口,长方形和正方形同属于四边形
public interface Quadrilateral {
    Double getLength(); // 获取长
    Double getWidth(); // 获取宽
}
// 长方形
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Rectangle implements Quadrilateral {
    private Double length;
    private Double width;
}
@Data
public class Square implements Quadrilateral {
    private Double side;
    @Override
    public Double getLength() {
        return side;
    }
    @Override
    public Double getWidth() {
        return side;
    }
}
// 测试类
public class Test {
    public static void main(String[] args){
        // 创建长方形对象
        Rectangle rectangle = new Rectangle(15.0, 10.0);
        // 扩宽
        resize(rectangle);
        // 15.0
        // 16.0
        print(rectangle);
    }
    // 扩宽修正方法
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }
    // 打印长宽
    public static void print(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }
}

In this way, objects of the Square class cannot use the resize() method.

7. The principle of synthetic reuse

The principle of composite reuse means: try to use association relationship such as combination or aggregation to realize it first, and then consider using inheritance relationship to realize it.

Generally, the reuse of classes is divided into two types: inheritance reuse and composition 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 will expose the implementation details of the parent class to the subclasses, and the parent class is transparent to the subclasses, this kind of reuse is also called "white box" reuse.
  • The subclass is highly coupled to the parent class. 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 extension and maintenance of the class.
  • It limits the flexibility of reuse. The implementation inherited from the parent class is static and defined at compile time, so it cannot be changed at runtime.

Combination or aggregation reuse can incorporate existing objects into new objects and make them part of new objects. New objects can call functions of existing objects. It has the following advantages:

  • It maintains the encapsulation of the class. Because the internal details of member objects are invisible to new objects, this kind of reuse is also called "black box" reuse.
  • The coupling between objects is low. Abstractions can be declared at member positions of a class.
  • The flexibility of reuse is high. This reuse can be performed dynamically at runtime, and new objects can dynamically refer to objects of the same type as member objects.

The following are examples of car types:

inheritance reuse
inheritance reuse

Synthetic multiplexing

Guess you like

Origin blog.csdn.net/pyy542718473/article/details/130685633