Tips: This article will take 4-5 minutes (little code)


Today, we are going to solve a problem:

How to write good code? Six principles of design patterns tell you

All the problems of life, knowledge gives you the answer.


== Single principle ==

Definition: There should be one and only one reason for a class to change.

The principle of singleness applies to interfaces, classes, and methods. For interfaces, we must design singleness, but for classes, we need to consider many aspects. Rote application of a single principle will cause a proliferation of classes and bring a lot of trouble to maintenance. And over-categorizing responsibilities can artificially add complexity to the system.

The most common is the Activity class, if we in the Activity network request operation, request and business processing, processing after the notification of View refresh, so that the Activity will become very bloated, the Activity’s responsibility is to display the View and deal with the interaction with the user, how to solve? Detach network request operations and service operations to notify Activity view changes through interface callbacks. MVP mode is recommended.

For the single principle, the recommendation is that interfaces must do a single responsibility, and that classes should be designed so that there is only one reason for change.

== Open and close principle ==

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

The definition of the open closed principle is very clear: software entities should be open for extension and closed for modification, which means that a software entity should change by extending, not by modifying existing code.

For example, there is a class ProductFactory that produces a bunch of products:

public class ProductFactory {
    private final static List<Product> productList=new ArrayList<>();

    static {
        Product product=new Product();
        product.setPrice(10);
        product.setName("Product A");
        productList.add(product);
        product=new Product();
        product.setPrice(10);
        product.setName("Product B");
        productList.add(product);
    }
    
    public List<Product> getProduct(a){
        returnproductList; }}Copy the code

The client can get the list of products by creating ProductFactory and calling getProduct method, which seems perfect. What if the product says I want to getProduct A at this time, and then A few days later, it needs to be discounted because the product benefit is not very high? Do you add a method in the ProductFactory class or modify it directly in that method? This class is frequently modified due to service changes.

To solve this problem, use the open/close principle. Create an interface IProduct and internally specify a rule:

public interface IProduct {
    List<Product> getProduct(a);
}
Copy the code

Normal product acquisition:

public class ProductFactory implements IProduct {

    private final static List<Product> productList=new ArrayList<>();

    static {
        Product product=new Product();
        product.setPrice(10);
        product.setName("Product A");
        productList.add(product);
        product=new Product();
        product.setPrice(10);
        product.setName("Product B");
        productList.add(product);
    }

    @Override
    public List<Product> getProduct(a) {
        returnproductList; }}Copy the code

Client use:

public class Client {
    public static void main(String[] args){
        IProduct productFactory=newProductFactory(); List<Product> productList=productFactory.getProduct(); }}Copy the code

The product manager said that I only need the product of product A to be realized by extension:

public class ProductAFactory implements IProduct {

    private final static List<Product> productList=new ArrayList<>();

    static {
        Product product=new Product();
        product.setPrice(10);
        product.setName("Product A");
        productList.add(product);
        product=new Product();
        product.setPrice(10);
        product.setName("Product B");
        productList.add(product);
    }

    @Override
    public List<Product> getProduct(a) {
        List<Product> list=new ArrayList<>();
        for(Product product:productList){
            boolean isProductA=product.getName().equals("Product A");
            if(isProductA){ list.add(product); }}returnlist; }}Copy the code

On the client side, we just need to replace ProductFactory with ProductAFactory. Even if the product says it needs to discount the product, we just need to create a discount class to implement IProduct and implement getProduct method to discount the product.

== Richter’s substitution rule ==

Definition: All references to a base class (parent class) must transparently use objects from its subclasses.

Richter’s substitution principle tells us that when software replaces a base class with its subclass object, the program will not generate any errors and exceptions; Otherwise, if a software entity uses a subclass object, it may not be able to use a base object.

All methods of a subclass must be declared in the parent class, or a subclass must implement methods declared in the parent class. According to Richter’s substitution principle, in order to ensure the extensibility of the system, the parent class is usually defined in the program.

In the application of Richter’s substitution principle, try to design the parent class as abstract class or interface, let the subclass inherit the parent class or implement the parent interface, and implement the methods declared in the parent class. At run time, subclass instances replace parent instances, and we can easily extend the functionality of the system.

== Dependent inversion principle ==

Definition: high-level modules should not depend on low-level modules; both should depend on abstractions. Abstraction should not depend on details, details should depend on abstractions.

In Java, abstraction refers to an interface or abstract class, neither of which can be directly instantiated.

Details are the implementation classes, and details are the result of implementing interfaces or inheriting abstract classes.

The high-level module is the calling side, and the low-level module is the concrete implementation class.

The concrete manifestation of dependency inversion is that the dependency between modules occurs through abstraction, and the dependency between implementation classes does not occur directly, but is generated through interfaces or abstract classes.

If classes depend directly on the details, they are coupled directly, so that when changes are made, they change the dependent code at the same time, limiting extensibility.

== Demeter principle ==

Definition: A software entity should interact with as few other entities as possible.

Demeter principle is also known as the least knowledge principle. In popular terms, it is to minimize the interaction between objects when designing a system. If two objects do not have to communicate directly with each other, then the two objects should not have any direct interaction.

If one of these objects needs to call a method of the other object, the call can be forwarded by a third party. Introduce a reasonable third party to reduce coupling between existing objects.

== Interface isolation rule ==

Definition: A class’s dependency on another class should be based on the smallest interface.

Create a single interface, not a large and bloated interface. Try to refine the interface and have as few methods in the interface as possible. That is, instead of trying to create a huge interface for all the classes that depend on it, we need to create interfaces that are specific to each class.

Interfaces should be small, but limited. Refinement of interfaces can improve the flexibility of program design, but excessive refinement can result in too many interfaces and complicate the design.