Most programmers can write code that computers can understand, but only good programmers can write code that people can understand

I have been assigned a project refactoring task in recent months. To be honest, project refactoring, including a lot of business code rewrites, can be a time-consuming and labor-intensive undertaking.

One might ask why bother refactoring and rewriting existing projects when they work just fine. The reason is more convenient for future maintenance.

Take my side of the old code, because of the hard work of many generations of programmers, so that the program can run normally, at the same time, another problem is chaos, especially chaos.

Forget about file names and stuff like that, but you can still find it by jumping through the first entry.

The point is that some of the business logic that goes along with your nonsensical function names really looks like crying. There was a module that had a bug that needed to be fixed, and the person in charge of the module happened to be on leave. So it took me and the leader half a day to locate the bug in thousands of lines of code. The bug isn’t that complicated.

Tip: I would have given priority to the problem code that might need refactoring.

Tip2: There will be more posts on code refactoring

Duplicate code

Top of the list of problematic code is not to mention duplicate code. The same logic appears in every function, and it’s annoying to watch.

Scenario a function with the same logic in more than one class

This is a comparative scenario and easy to solve, where duplicate code is pulled out and placed in a public class such as Utils

Scenario 2 inherits the same logic as multiple subclasses of the same parent class

Push the same logical code into the parent class. The most common is BaseActivity and its children, and a lot of common logic can be written into BaseActivity.

In summary, duplicate code should be the best solution, as long as most of the business code does not repeat itself.

Overlong function

Simply put, there are dozens of lines of function methods. Functions that are too long can have the following problems.

  • Too many local variables (more variables means more uncontrollable factors)
  • Too deep indentation levels (if, while, too many interface callbacks can cause this problem)
  • Overly complex business logic (not easy to understand and prone to error)

Such as

Private void goOrder(){private void goOrder(){private void goOrder(){private void goOrder(){Copy the code

A more appropriate approach would be

Private void goOrder(){int orderType=getOrderType(); // Get the payment type payOrder(orderType); // Pay the order}Copy the code

Short and meaningful functions are easier to understand. When extracting code, be careful that the piece of code you extract has sufficient meaning. Such as getting some data, such as drawing a base map, such as creating a necessary object.

The name of the new function should be sufficient to express the function of the function body. Simple function names are usually expressed as verbs and nouns. Something like createBitmap.

If the function name is too long, you can use abbreviations, but be careful not to erase the meaning of the word. One of the projects I worked on was to write startActivity as startAC. I don’t know what it means at first sight. If you really want to use shorthand, you might as well write something like goActivity.

Too large a class

I’m sure you’d rather see several classes with hundreds of lines of code than a class with thousands of lines of code.

Sometimes it doesn’t make sense to try to solve a problem with a class. There are a lot of problems when a class gets too big,

  • Too many global variables, for the program to increase a lot of unstable factors
  • Too many functions can be difficult to read, as mentioned at the beginning of this article because the class is too long
  • Code coupling becomes stronger, with lots of code that should be common jammed together
  • The difficulty of maintenance and modification becomes higher in different degrees

A more appropriate solution is to split a class, a class as large as 5,000 lines of code, into multiple classes with different functions. Note that the split class must have its own core logic. Or processing the data, or presenting the data.

Of course, one of the negative consequences of splitting classes is that the total amount of code will increase. Much of this added code is to maintain connections between several classes.

There are a few more lines of code, but there are a lot fewer global variables than there are in the seemingly clean code logic. More code doesn’t matter.

Too long parameter list

The previous article mentioned that too many global variables can introduce an undetected bug that can only be passed into a function as arguments. That’s what we usually do.

But this can lead to another problem: if a function takes a lot of arguments to execute, you have to write down a long list of arguments. Long argument lists are less buggy, but harder to read.

If you look closely at the function, you will find that most of the parameters required by the function can be taken from an object, so you just pass in the corresponding object.

“What?” You say that the parameters required by your function cannot be taken directly from an object. At this point, either you assemble an object yourself, or there is something wrong with your code structure.

Coupling is too strong

When you need to add a new feature, you may find that you need to change a lot of code in addition to the code that added the feature. This means that your code is too coupled.

There are two kinds of coupling problems

  1. Every time you add or change a feature, you need to change multiple methods in a class. At this point, the frequently changed methods should be extracted
  2. Every time you add or change a feature, you need to change multiple methods in multiple classes. You can also separate out frequently changed methods and place them in a new class.

Too many entity classes

During development, you often need to define entity classes to hold objects. Especially in network requests, there is almost one interface for one entity class.

With so many interfaces and the need for entity objects in your native code, you’ll find that the entity class code takes up too much volume, and you’ll need to make adjustments.

If you look closely, you will find that many interfaces return only a few important fields, such as ID, name, time, etc.

It’s just possible that the server wasn’t consulted beforehand, and this is what happens

Interface A returns ID, name, time. Interface B returns TID, TNAME, ttime

In fact, the data structure returned by interfaces A and B is exactly the same.

In this case, the @serializedName annotation is needed so that there is no need to define two entity classes for interfaces A and B. Just define it as follows

@SerializedName("tid")
private String id;
@SerializedName("tname")
private String name;
@SerializedName("ttime")
private String time;    
Copy the code

For example, if interface A returns ID, name, and interface B returns ID, name, time, you can define only one entity class, including the ID, name, and time fields.

Also, when I define the entity class, I’m going to break it down into the basic entity class, and the interface return entity class. This will make it a little bit clearer.

Don’t leave useless code behind

It’s not uncommon in your daily work to see a large section of code in someone else’s code with comments next to it that it might be used in the future. In practice, however, the commented out code is largely unused.

The reason for this is that many people don’t want to delete code that they’ve worked so hard to write, with the expectation that they’ll need it.

Regardless of whether to use it or not, I personally feel that I see more commented out code in some code than normal code, and the head is too big.

Since the program works, it means that the commented code is useless and can be left unread. Unless the annotated code is usable in the foreseeable future. Otherwise it should be deleted.

Even if you suddenly need to add this feature later, you can add it later. You’ve already written it, and there’s no problem writing it again.

Don’t reserve an entrance for the unforeseen future

A classic scenario is when an interface is redefined with multiple methods defined in advance. A typical example is a list of items such as RecyclerView events.

ItemClick, itemLongClik, XXX, and so on are predefined when defining interface methods, but some methods are not used at all.

When people ask why this implementation method has no body, you always say that this can be saved for later use. But this may not be able to wait until the project closes down.

So don’t leave any room for the foreseeable future. Of course this is different if the requirements are already added to the task timeline. In this case, you have to make room for future requirements.

Remove/merge some of the less important classes

If a class’s function can be replaced, then you can consider merging it into another class.

More commonly, we define more utils classes, such as StringUtils, ToastUtils, ScreenUtils, etc. However, the number of methods in these classes is not very large. In this case, you can consider merging them into one utils, such as CommonUtils, instead of the above utils.

Too many comment classes

I think most of the time code is written for people to read, and it’s important to have a name that makes sense in order for people to understand it, but sometimes you need to add comments because there’s no proper name, or the name is too long, or the name doesn’t convey the meaning of the function.

So what kind of comments are appropriate?

Error model

// This is id private String ID; // This is the name private String name;Copy the code

This obvious variable, if annotated, is a bit like a foot-binding that gets longer and longer.

Wrong Demonstration 2

Private void getUserInfo(){}Copy the code

This error is the same as the one above, as long as the function name is appropriate, no comment is required at all

Wrong Demonstration 3

/ * * * * * * * * * * * / / 2017.9.1 added modified the XXX XXX / 2017.9.10 / * * * * * * * * * * * /Copy the code

Some people have a habit of adding these modifications to the name of a class or function, and I have encountered them in projects. In fact, these notes are useless, and too many will make reading difficult. And now there are powerful version management tools like SVN/Git, so you can see who submitted the code and what was changed.

Correct demonstration

LoginByPhone ==0 loginByPhone; //code==1 loginByQRCode ; code==2 loginByLauncher private void login(int code){ loginByPhone/loginByQRCode/loginByLauncher }Copy the code

In the example above, you only know that the function name is a login operation, but the meaning of the code parameter is not clear. In this case, you need to add a comment to explain the meaning of the different code values.

In fact, this example is not very suitable, and a more appropriate approach would be to define several constants to determine what process is being executed.

The purpose of this example is to show that when a function name does not fully express the meaning of the function and the function is prone to misunderstanding, it is necessary to add a comment, and it is best to write the comment completely.

Correct Demonstration 2

You can refer to the source code comments, Android source code comments will be more clear description of the function.

Things like when you can call a function and when you can’t. What does the return value mean? Such comments are comfortable to look at.

Write at the end of the article: If readers have any questions or comments, feel free to point them out in the comments.