Today I’m going to talk to you about some code refactoring tips, partly from books and partly from some hands-on experience in LZ maintenance projects.

There are many methods of reconstruction. Relatively speaking, the coverage of an article cannot mention all of them. LZ can only put forward some methods that are often used in daily life, like some high-end methods.

Without further ado, let’s take a look at the list of useful refactoring techniques.

No.1: Refinement of repetitive code

Repeating code is one of the most effective ways to refactor, and the reason for doing it goes without saying. It has a number of obvious benefits, such as greatly reducing the total amount of code, easy maintenance, and more legible code. A complete PDF version of the Java Interview handbook has been organized into a document

Its focus is to find duplicate code in the code that completes a sub-function, and then move it to the appropriate method and put it in the appropriate class without hesitation.

Small instance

Class BadExample {public void someMethod1(){//code system.out.println (" duplicate code "); //code} public void someMethod2(){//code system.out.println (" duplicate code "); / * repeat * / / / a code block code}} / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / class GoodExample {public void someMethod1(){ //code someMethod3(); //code } public void someMethod2(){ //code someMethod3(); //code} public void someMethod3(){system.out.println (" duplicate code "); /* Duplicate code block */}}Copy the code

No.2: Segmentation of verbose methods

In fact, the segmentation of lengthy methods sometimes has an inseparable relationship with the extraction of repeated code, often in the process of refining repeated code, we unknowingly completed the segmentation of a super-long method. If some verbose methods are still around after you’ve refined most of the repetitive code, it’s time to focus on those verbose methods.

One thing to note about this is that since most of the time when we split a large method, it is important to give each of the submethods a proper method name. It can be said that the ability to give a method a good name can sometimes reflect the general level of a programmer.

Small instance

class BadExample { public void someMethod(){ //function[1] //function[2] //function[3] } } /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / class GoodExample {public void someMethod () {once function1 (); function2(); function3(); } private void function1(){ //function[1] } private void function2(){ //function[2] } private void function3(){ //function[3] } }Copy the code

No.3: Optimization of Nested Conditional Branches (1)

A large number of nested conditional branches is very intimidating code and should be avoided at all costs. Although the structuration principle always says that a function can only have one exit, with so many nested conditional branches, let’s forget the rule.

There’s a technical term called predicate sentences that can cure this dreaded nested conditional statement. 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 judgment behind. After this procedure, the code will look very clear. LZ gives you a classic example below, and you can judge for yourself which of the two methods makes you look clearer.

Small instance

class BadExample { public void someMethod(Object A,Object B){ if (A ! = null) { if (B ! = null) {/ / code [1]} else {/ / code [3]}} else {/ / code [2]}}} / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / class GoodExample { public void someMethod(Object A,Object B){ if (A == null) { //code[2] return; } if (B == null) { //code[3] return; } //code[1] } }Copy the code

No.4: Optimization of Nested Conditional Branches (2)

The nested conditional branch here is a little different from the above, in that it cannot be optimized using predicate clauses, but should be merged to achieve code clarity. It can also be seen from these two points that nested conditional branches should be avoided in coding as they can greatly reduce the readability of the code.

Below please still and unknown sense li ape friend see below this typical small example.

Small instance

class BadExample { public void someMethod(Object A,Object B){ if (A ! = null) { if (B ! = null) {/ / code}}}} / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / class GoodExample {public void someMethod(Object A,Object B){ if (A ! = null && B ! = null) { //code } } }Copy the code

No.5: Remove one-time temporary variables

In our daily life, we all often use disposable chopsticks, which is undoubtedly damaging to trees. In a program, however, one-off temporary variables are not only a minor detriment to performance, but also a violation of code readability. So we need to operate on one-off temporary variables.

Small instance

class BadExample { private int i; public int someMethod(){ int temp = getVariable(); return temp * 100; } public int getVariable(){ return i; }} / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / class GoodExample {private int I; public int someMethod(){ return getVariable() * 100; } public int getVariable(){ return i; }}Copy the code

No.6: Eliminate long parameter lists

Methods that pass a large number of parameters are unacceptable to programmers who want clean code. We can try to get rid of the long argument list by encapsulating these arguments as an object passed to the method. Most of the time, when you try to find such an object, it already exists, so most of the time, we don’t need to do any extra work.

Small instance

class BadExample { public void someMethod(int i,int j,int k,int l,int m,int n){ //code } } /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 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 }Copy the code

No.7: Extract constants from a class or inheritance system

The purpose of this refactoring is to eliminate some magic numbers, string constants, etc., which, of course, can be confusing to the program’s intent. As for the elimination of string and other types of constant, more benefits lie in the convenience of maintenance. Because we only need to change one constant, we can change all the code in the program that uses that constant.

Incidentally, the most common and similar case is the extraction of INPUT, LIST, SUCCESS, and other constants from the Action base class.

Small instance

Class BadExample {public void someMethod1(){send(" Your operation succeeded!" ); } public void someMethod2(){send(" Your operation succeeded!" ); } public void someMethod3(){send(" Your operation was successful!" ); } private void the send (String message) {/ / code}} / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 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 } }Copy the code

No.8: Let the class provide the methods it should

Most of the time, we will often manipulate most of the attributes of a class to get the result we want. In such cases, we should let the class do its job, not let us do it for it. And most of the time, this process ends up being a source of duplicate code.

Small instance

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; }}} / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 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; }}}Copy the code

No.9: Break up long classes

This technique is actually a very practical one, but it is relatively difficult, so LZ ranked it behind. It is difficult for LZ to give a simple and easily illustrated example of this technique, as it is no longer just a small technique.

Most of the time, our focus on splitting a class should be on the attributes of the class. The split sets of properties should be logically separable, and the use of both sets of properties should be concentrated in some method in the code. If there are properties that exist in both sets of methods, you can resolve this dependency by passing parameters.

Class splitting is a relatively large project, after all, a class is often used by many classes in the program, so this refactoring is quite difficult, must be careful, and do adequate testing.

No.10: Extract repeated properties and methods from the inheritance system to the parent class

Most of the time this technique requires a lot of judgment, and most of the time it’s actually a step towards the template approach pattern. Instance LZ is not given here, because its small instances would be meaningless, except that subclasses have the same properties or methods, and then remove the duplicate properties or methods of subclasses and put them in the parent class.

This type of refactoring is usually not trivial, so this one, like Type 9, requires a lot of care and testing. And you should only use this technique if you are sure enough that the attributes or methods extracted from the parent class should be common to the subclasses.

conclusion

Since LZ’s current work is to maintain a relatively old project, LZ has almost tried all the above ten methods, and fortunately the results are all good. A complete PDF version of the Java Interview handbook has been organized into a document

The last two are too closely related to the actual situation for LZ to give simple examples, but the latter two are not common refactoring techniques, so they are acceptable. However not commonly used does not mean unimportant, you ape friends still want to know this point. In addition, LZ would like to say that the above example is just a simple demonstration of the approach. In practice, the structure of the code can be very strange, but it is always the same. It is not difficult, therefore, to navigate safely through the chaos by grasping the core of each technique.

Well, this small share to end here, I hope you ape friends if you feel harvest, can recommend to encourage LZ, by the way also let more people see. In this way, maybe every project code we take over will not be very bad, but also give project maintainers like LZ a way to live.