preface

I believe that those of you who have experience in development have experienced this: you are asked to take over a project, two situations. A. This project has been developed and maintained by several people, even generations of programmers; B. This project is waiting for your new development. If you don’t have a deadline, which one would you prefer? I believe 99.9% of people will choose B. Is there…? According to? Cause n bugs because you don’t want to change one bug. , in the end, it is just because of the existing projects architecture was not well done, or not timely structure adjustment, if you take the old code, may be in order to add a function, because the architecture does not have scalability, you probably can only be revised on the basis of the original a few lines of code, and even modify the hundreds of thousands of lines of code to achieve a goal, In order to bury a lot of hidden trouble for the next round of chivalry. So that brings us to today’s topic? What kind of code is extensible?

In this paper, the author xiong_it, blog link: http://blog.csdn.net/xiong_it. Please indicate the source of reprint.Copy the code

This article has been published exclusively by guolin_blog, an official wechat account.

Open Close Principle

OCP principle (Open closed principle) : a software entity such as classes, modules, and functions should be open for extension and closed for modification.

wtf??? Too abstract!! In my understanding, THE OCP is the supreme framework of the six principles, which is why it is so abstract and difficult to understand. In object-oriented languages, OCP is one of the most abstract interface, and the rest of the five principles is a subclass of OCP interface, together they define the OOP development standards of the world, commonly used in the 23 design patterns is only counted as the six principles of the implementation of the abstract class, let’s develop the code of practice is the real concrete subclass.

public interface OCP {
    void openExtention();
    void closeModifiability();
}Copy the code

Q: What is OCP? A: What is OCP? It tells us that we should write code that is open to extension and not try to embrace requirements change by modifying existing code. Here, code can refer to a function module, class, or method. Q:Why do we need to follow this principle? A: Why do we follow the OCP principle? Everyone knows the pain of changing requirements late in code, and if extensions are not used to accommodate the change, the code will be riddled with changes. Q:How do we practice this principle? A: How can we put this principle into practice? Don’t use concrete classes if you can use abstract classes, and don’t use abstract classes if you can use interfaces. The bottom line: Try to program to the interface. The reason I say “as much as possible” is because everything has its limits, so you don’t have to say hello world and you have to implement the whole interface again.

Talk is cheap, show your code.

Needs: Lao Wang drives to the northeast.Copy the code

Easy, masturbation.

Lao Wang is here. Everybody hide your wife.

public class Laowang {
    private Car car;
    private DongBei dongbei;

    .
    getter() & setter()
    .public void drive() { car.goto(dongbei); }}Copy the code

I’ll give you a car if you want one

public class Car {
    public void goto(DongBei dongbei) {
        System.out.println(" Going to the northeast, la la la ");// Simulate a car trip to consume time. 10 seconds to the northeast, can drive fast enough ah! Did the driver use to fly a plane?
        Thread.getCurrentThread().sleep(10 * 1000);
        System.out.println(" Destination northeast arrived "); }}Copy the code

In the northeast

public class DongBei {
    private String address = "The Ga Da da northeast.";
}Copy the code

The old driver is going to start, so clock in quickly. Hey, student card. Hey, hey, hey, you got the wrong card.

public static void main(String[] args) {
    Car car = new Car();
    Laowang wang = new Laowang();
    wang.setCar(car);
    DongBei dongbei = new DongBei();
    wang.setDongbei(dongbei);
    wang.drive();
}Copy the code

Perfect, Perfect! Now the demand has changed, Lao Wang has achieved the small goal set in 2017, earned 100 million yuan and bought a private plane. He doesn’t want to drive to northeast China, it’s too low, he wants to fly a plane to northeast China.

Requirement 2: Lao Wang flies a plane to northeast ChinaCopy the code

Simple, add an attribute to Lao Wang, add a few methods to achieve it? The code doesn’t work. OK, another perfect change! ? The demand has changed again, Lao Zhang and Lao Wang grew up wearing the same crotch, Lao Zhang saw Lao Wang now on the plane, can he borrow his car to drive?

Requirement 3: Lao Zhang drives to northeast ChinaCopy the code

This, this, this, this, simple, re-stroked wang in need of 1 code on the line, not just change the name of the matter? After a while, the demand changed again. Lao Zhang had something urgent to go to northeast China, so Lao Wang lent Lao Zhang the plane.

Requirement 4: Lao Zhang flies a plane to northeast ChinaCopy the code

Here, here, here, here, here, here, here, here, here, here, here, simple, just rub wang’s code in Requirement 2 again. After a while, the demand changed again. Instead of going to northeast China, Lao Wang wanted to fly to Guangdong to visit his father-in-law.

Demand 5: Lao Wang flies to Guangdong Demand 6: Lao Zhang drives to Guangdong Demand 7: Lao Wang flies to America Demand 8: Xiao Wang drives to Xizang demand...Copy the code

This, this, this, this, this, this, this, this, R&D kid spits old blood on the screen, dead, 25 years old.

Here, the author suggests that people, vehicles, destinations abstraction, interface, can adapt to frequent changes in demand.

On the class diagram



The client code is adjusted as follows:

public static void main(String[] args) {
    // If you want to use a local vehicle to get around, you can use a new car
    ITransportation car = new Car();
    // It is Lao Wang who is going out this time
    Person wang = new Laowang();
    // Lao Wang chooses to drive
    wang.setTransportation(car);
    // Lao Wang's destination is northeast China
    AbsDestination dongbei = new DongBei();
    dongbei.setAddressName("Northeast");
    wang.setDestination(dongbei);
    // The old driver just drove off
    wang.startOff();
}Copy the code

Lao Wang’s code is as follows

public class Laowang extends Person{.public void startOff() {
        this.transportation.transport();
        System.out.println("Here we go.");
        //thread.sleep();
        System.out.println("Destination" + this.destination.getAddressName() +"Come."); }}Copy the code

The results are as follows:

Here we go. Northeast of our destination.Copy the code

Does the client code change much less now as requirements change?

Note: the open closed principle is open for extension and closed for modification, but does not mean that no modification is made. Changes in low-level modules must be coupled by high-level modules, otherwise it is an isolated and meaningless code fragment. In the case of business rules change, the high-level module must be partially changed to adapt to the new business, and the change should be as little as possible to place the spread of risk of change — Qin Xiaobo, Zen of Design Pattern

Single Responsibility Principle

SRP principle (Single responsibility principle) : There should be one and only one reason for a class to change.

public interface SRP extends OCP {
    void onlyDoOneThing();
}Copy the code

In layman’s terms, a class, a method should only do one thing. For two chestnut: 1. When A class A R1, R2 two responsibilities, when the responsibilities of R1 change, you need to modify the class A, when R2 is changed, you need to modify the class A, then, there have been two reasons may cause the change of the class, class A has not been A single responsibility, duties will need to break up, such as split into class A1, A2: A1 is responsible for R1, A2 is responsible for R2. 2. Another example is method M, which is responsible for both computation and printing

public void M(int a, int b) {
    int c = 0;
    c = a + b;

    System.out.println("It prints =" + c); 
}Copy the code

One day, you want to change the calculation rules to

c=a+b+1;Copy the code

At this point, you modify method M. Another day, you want to change the printing rules to

System.out.println("It prints ="(c + +1)); Copy the code

You have modified M, and now there are more than two reasons for you to modify it, so this method should be split into two methods: calc method and print method. Like each method only does one thing.

So how does it scale? Take the most common design of ImageLoader in Android as an example. ImageLoader mainly needs to realize two functions: download images and cache images. What if we put all the functionality in a single ImageLoader class, assuming we change the way we download it? Cache policy change? If you want to change ImageLoader all the time, how do you make sure that if you change one feature, the other will remain intact and not be corrupted? Split the responsibilities, use the ImageCache interface and its subclasses for caching, and associate with ImageLoader. Once the responsibilities are single, you can change the related code in each single responsibility class, which greatly reduces the probability that other functions will be contaminated.

Of course, this is just a random example, and the degree to divide a single responsibility is difficult to grasp, and everyone needs to make a judgment based on their own situation and project situation.

Liskov Substitution Principle

OCP principle (Richter’s Substitution principle) : all references to a base class must be able to transparently use objects from its subclasses

public interface LSP extends OCP {
    void liskovSubstitutionPrinciple();
}Copy the code

In plain English: subclasses can appear wherever the parent class can, and replacing them with subclasses does not generate any exception errors, and vice versa. This is mainly reflected in the fact that we often use abstract/base classes as method arguments, and it is up to the caller to decide which subclasses to pass in as arguments.

This principle includes the following aspects:

  • A subclass must fully implement the methods of its parent class
  • Subclasses can have their own personality appearance (properties) and behavior (methods)
  • When overriding or implementing a superclass method, the parameters can be amplified. That is, when a method parameter of the parent class is HashMap, the subclass parameter can be HashMap, Map or larger
  • When overwriting or implementing a method of a parent class, the return result can be reduced. That is, a method of the parent class returns type Map, and a subclass can be Map, HashMap, or smaller.

Dependence Inversion Principle

DIP principle: high level modules should not depend on low level modules, so dependencies should be abstract. Abstraction should not depend on concrete details, but concrete details should depend on abstraction

Low-level module: The indivisible atomic logic is low-level module. High-level module: the assembly of low-level modules is the high-level module

Abstract: Represented in Java as base classes, abstract classes, interfaces, not just abstract class details: represented as subclasses, implementation classes

In layman’s terms, the principle contains the following elements:

  • Dependencies between modules should occur through abstraction, and there should be no dependencies between concrete implementation classes
  • An interface or abstract class does not depend on the implementation class, otherwise abstraction is lost
  • Implementation classes depend on interfaces or abstract classes

In a word: “Interface oriented programming”.

Interface-Segregation Principle

The ISP Principle (interface Isolation Principle) : a client should not rely on interfaces it does not need; Dependencies between classes should be based on minimal interfaces

To put it simply: when using an interface, you should create a single interface, not a bloated interface, and try to provide a dedicated interface to the caller, rather than a multi-functional interface.

Here I would like to give an example of an Android event handler Listener design. As you all know, when we want to add a button click event, we can use the following code

button.setOnClickListener(clickListener);Copy the code

When you want to add a long-press event to it, you can use the following code

button.setOnLongClickListener(longClickListener);Copy the code

There are other event interfaces like OnTouchListener and so on. Why doesn’t it just provide a generic interface, IListener? This is the result of following the ISP principle by providing so many independent interfaces. Each interface is minimized. As the Activity/button caller, I can selectively process the events I want to process, and I don’t care about the events Listener.

Low of Demeter

LoD (Demeter’s Law) : Also known as the Least Knowledge Principle (LKP), one object should know the Least about other objects.

In layman’s terms: a class should know as little as possible about the class it needs to be coupled or called. It has nothing to do with me inside the class being coupled or called. If I don’t need something, don’t make it public.

Demeter’s law contains the following elements:

  • Communicate only with friend classes: couple only that coupled class
  • There is also distance between friends: reduce methods that should not be public and provide a concise access to the outside world
  • You create your own method: as long as it doesn’t add internal overhead or inter-class coupling

Thanks and references

Xiao-bo qin: zen “design patterns” Mr. Simple: the Android source code design pattern analysis and the actual combat java-my-life:www.cnblogs.com/java-my-lif… … …

The latter

Rules are just rules, we should not adhere to the rules, should hold a dialectical attitude to view these six principles, in order to better achieve the purpose of practice and application. Thanks to the standardized guidance of the above authors and bloggers and the blogs of many bloggers, I gradually learned to practice design patterns and application architecture. I will update the Design Patterns series in the Android blog in the future, and I will refer to the above books and blogs in the following blogs. Welcome friends to comment on the “like” brick exchange. To view an updated version of this article, please click: Xiong -it. Github. IO

Follow-up: Design Patterns in Android: A Directory Guide