Which of the 10 most practical tips for refactoring have you used?

LZ has been studying the source code of virtual machine recently. Unfortunately, at present, it is just a little bit of a clue, which can't be shared with you. The huge source code of JAVA virtual machine can't be solved overnight. LZ is ashamed.

This time, let's put aside the JAVA virtual machine source code, which is relatively low-level. LZ will discuss with you a few tips for code reconstruction. Some of these contents are from books, and some are from the practical experience of LZ maintenance project. If ape friends have ever used this technique, they may as well participate in the message of the article, share your small experience and experience with others, or use it to impact LZ's own defined ranking, LZ is not very welcome.

There are many ways to reconstruct. Relatively speaking, the coverage of an article cannot be mentioned. LZ can only put forward some methods that are often used in ordinary times, such as some high-end methods. If you are interested, you can go to some special books for hunting.

In addition, because LZ is a JAVA developer, part of the tips for refactoring may be closely related to JAVA language, or object-oriented language. However, most of the tips, whether process oriented language or object-oriented language, are common to each other.

Let's take a look at the list of practical refactoring techniques.

No.1: refining repetitive code

Repetitive code is one of the most effective methods of refactoring, and the reason for this refactoring is needless to say. It has many obvious advantages, such as greatly reduced the total code, convenient maintenance, and more clear and readable code organization.

Its focus is to find the repeated code among the codes to complete a certain subfunction. After finding it, please do not hesitate to move it to the appropriate method and store it in the appropriate class.

Small example

class BadExample {

    public void someMethod1(){
        //code
        System.out.println("Duplicate code");/* Repeated code block */
        //code
    }

    public void someMethod2(){
        //code
        System.out.println("Duplicate code");/* Repeated code block */
        //code
    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    public void someMethod1(){
        //code
        someMethod3();
        //code
    }

    public void someMethod2(){
        //code
        someMethod3();
        //code
    }

    public void someMethod3(){
        System.out.println("Duplicate code");/* Repeated code block */
    }

}

No.2: segmentation of lengthy methods

As for the segmentation of lengthy methods, sometimes it has an inseparable relationship with the refining of repeated code. In the process of refining repeated code, we unconsciously complete the segmentation of a super long method. If, after you've refined most of the repetitive code, some lengthy methods remain, you need to settle down to deal with them.

It is worth noting that most of our segmentation of a large method is aimed at some of its subfunctions, so we need to give each subfunction a proper method name, which is very important. It can be said that whether a good name can be given to a method can sometimes reflect the general level of a programming ape.

Small example

class BadExample {

    public void someMethod(){
        //function[1]
        //function[2]
        //function[3]
    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    public void someMethod(){
        function1();
        function2();
        function3();
    }

    private void function1(){
        //function[1]
    }

    private void function2(){
        //function[2]
    }

    private void function3(){
        //function[3]
    }

}

No.3: optimization of nested conditional branches (1)

A large number of nested conditional branches are easily prohibitive code. We should try our best to avoid such code. Although the structural principle has always said that a function can only have one exit, under such a large number of nested conditional branches, let's forget the so-called rule.

There is a professional term Wei sentence, which can cure this horrible nested conditional sentence. Its core idea is to put the situation that does not meet certain conditions in front of the method, and jump out of the method in time, so as not to affect the later judgment. After this operation, the code will look very clear. LZ will give you a classic example. You can judge these two ways by yourself, which makes you look more clear.

Small example

class BadExample {

    public void someMethod(Object A,Object B){
        if (A != null) {
            if (B != null) {
                //code[1]
            }else {
                //code[3]
            }
        }else {
            //code[2]
        }
    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    public void someMethod(Object A,Object B){
        if (A == null) {
            //code[2]
            return;
        }
        if (B == null) {
            //code[3]
            return;
        }
        //code[1]
    }

}

No.4: optimization of nested conditional branches (2)

The nested conditional branches mentioned here are slightly different from the above ones. They cannot be optimized by using satellite statements, but should be combined to achieve the purpose of clear code. It can also be seen from these two articles that nested conditional branches should be avoided in coding, which will greatly reduce the readability of the code.

Let's take a look at the following typical example for the ape friends who are not aware of it.

Small example

class BadExample {

    public void someMethod(Object A,Object B){
        if (A != null) {
            if (B != null) {
                //code
            }
        }
    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    public void someMethod(Object A,Object B){
        if (A != null && B != null) {
            //code
        }
    }

}

No.5: remove one-time temporary variables

In our life, we often use disposable chopsticks, which is undoubtedly the destruction of trees. However, in the program, the one-time temporary variable is not only a little damage to the performance, but also a blasphemy to the readability of the code. Therefore, it is necessary to operate on some disposable temporary variables.

Small example

class BadExample {

    private int i;

    public int someMethod(){
        int temp = getVariable();
        return temp * 100;
    }

    public int getVariable(){
        return i;
    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    private int i;

    public int someMethod(){
        return getVariable() * 100;
    }

    public int getVariable(){
        return i;
    }

}

No.6: eliminate long parameter list

For some methods that pass a large number of parameters, it is unacceptable for the program apes who pursue clean code. We can try to wrap these parameters into an object and pass it to the method, so as to remove the long parameter list. In most cases, when you try to find such an object, it often already exists, so in most cases, we don't need to do extra work.

Small example

class BadExample {

    public void someMethod(int i,int j,int k,int l,int m,int n){
        //code
    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    public void someMethod(Data data){
        //code
    }

}

class Data{

    private int i;
    private int j;
    private int k;
    private int l;
    private int m;
    private int n;

  //getter&&setter

}

No.7: extract constants in a class or inheritance system

The purpose of this refactoring is to eliminate some magic numbers or string constants and so on. The disadvantages brought by magic numbers, needless to say, will confuse the intention of the program. For the elimination of constants of string and other types, the more advantage lies in the convenience of maintenance. Because we only need to modify a constant, we can complete the modification of all the code using the constant in the program.

By the way, the most common and similar situation is the extraction of INPUT, LIST, SUCCESS and other constants in the Action base class.

Small example

class BadExample {

    public void someMethod1(){
        send("Your operation succeeded!");
    }

    public void someMethod2(){
        send("Your operation succeeded!");
    }

    public void someMethod3(){
        send("Your operation succeeded!");
    }

    private void send(String message){
        //code
    }
}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    protected static final String SUCCESS_MESSAGE = "Your operation succeeded!";

    public void someMethod1(){
        send(SUCCESS_MESSAGE);
    }

    public void someMethod2(){
        send(SUCCESS_MESSAGE);
    }

    public void someMethod3(){
        send(SUCCESS_MESSAGE);
    }

    private void send(String message){
        //code
    }

}

No.8: let the class provide the method that should be provided

Most of the time, we often manipulate most of the properties of a class to get the final result we want. At this time, we should let this class do what it should do, rather than let us do it for it. And most of the time, this process eventually becomes the source of repetitive code.

Small example

class BadExample {

    public int someMethod(Data data){
        int i = data.getI();
        int j = data.getJ();
        int k = data.getK();
        return i * j * k;
    }

    public static class Data{

        private int i;
        private int j;
        private int k;

        public Data(int i, int j, int k) {
            super();
            this.i = i;
            this.j = j;
            this.k = k;
        }

        public int getI() {
            return i;
        }

        public int getJ() {
            return j;
        }

        public int getK() {
            return k;
        }

    }

}

/* ---------------------Dividing line---------------------- */

class GoodExample {

    public int someMethod(Data data){
        return data.getResult();
    }

    public static class Data{

        private int i;
        private int j;
        private int k;

        public Data(int i, int j, int k) {
            super();
            this.i = i;
            this.j = j;
            this.k = k;
        }

        public int getI() {
            return i;
        }

        public int getJ() {
            return j;
        }

        public int getK() {
            return k;
        }

        public int getResult(){
            return i * j * k;
        }

    }

}

No.9: split verbose classes

In fact, this technique is also a very practical one, but because of its relatively high difficulty, LZ ranks behind. For this skill, LZ is difficult to give a simple and easy to explain small example like the above one, because it is not only a small hand.

Most of the time, the focus of splitting a class should be on the properties of the class. The split two batches of attributes should be logically separable, and in the code, the use of these two batches of attributes is also focused on some methods. If there are some properties existing in the two split methods at the same time, the dependency can be solved by parameter passing.

Class splitting is a relatively large project. After all, a large class is often used by many classes in the program. Therefore, this refactoring is quite difficult. We must be careful and do enough tests.

No.10: extract duplicate attributes and methods from inheritance system to parent class

Most of the time, this skill needs enough judgment. Many times, this is actually the process of moving towards the template method pattern. Its instance LZ can't be given here because its small instance will be meaningless, except that the subclass has the same property or method, and then delete the repeated property or method of the subclass and put it into the parent class.

Often this kind of refactoring is not a small project, so this kind of refactoring is similar to the ninth one, which needs enough care and testing. And you need to be sure that the attributes or methods extracted into the parent class should be the commonness of the child class before you can use this technique.

Concluding remarks

Since the current work of LZ is to maintain a relatively old project, LZ has almost tried 11 of the above ten methods, and the gratifying thing is that the effect is not bad.

Limited to the last two are too closely related to the actual situation, LZ can not give a simple example, but the latter two are not commonly used reconstruction techniques, so they are also acceptable. But not often doesn't mean it doesn't matter. You ape friends still need to know this. In addition, LZ also said that the above example is just a simple display of technique. In practical application, the structure of code may be strange, but it can never be separated from its origin. Therefore, as long as we grasp the core of each technique, it is not difficult to pass through these disorderly armies safely.

Well, this is the end of this sharing. I hope that if you think you have something to gain, you can recommend LZ for encouragement, and let more people see it by the way. In this way, maybe every project code we take over will not be very bad. It is also a way for project maintainers like LZ.

  • END -

Finally, I wish you all the technology in the precipitation sublimation.

Tags: Java Programming

Posted on Sat, 14 Mar 2020 07:30:08 -0700 by millsy007