"Refactoring to Improve the Design of Existing Code 1" Refactoring Principles

Preface

Refactoring the design of the existing code, a classic sacred book. I started it two years ago and read it half a year ago. It feels normal. Starting today, I will read it again, I hope there will be a different harvest!

/**
* @startTime 2020-12-16 23:22
* @endTime 2020-12-16 23:59
* @startPage 1 
* @endPage 55
* @efficiency 56/1 = 56页/天
* @needDays 412/56 = 7天
* @overDay 2020-12-16 + 7天 = 2020-12-22 
*/

Chapter 1. Refactoring, the first case

The so-called refactoring is to modify the code without changing the external behavior of the code to improve the internal structure of the program. Refactoring is an orderly program finishing method formed through hard work, which can minimize the chance of introducing errors in the finishing process. Essentially, refactoring is to improve the design of the code after it is written.

Any fool can write code that a computer can understand, and only a code that humans can understand is an excellent programmer.

Chapter 2 Refactoring Principles

1. What is refactoring

An adjustment to the internal structure of the software, the purpose is to improve the comprehensibility and reduce the cost of modification without changing the observable behavior of the software.

The purpose of refactoring is to make the software easier to understand and modify.

The harder it is to see the design intent of the code, the harder it is to protect the design. Frequent refactoring can help the code maintain its proper form. To accomplish the same thing, poorly designed programs often require more code. This is often because the code uses exactly the same statements in different places to do the same thing. Therefore, an important direction to improve design is to eliminate duplicate code. The importance of this action is to facilitate future modifications. Reducing the amount of code does not make the system run faster, because it has almost no obvious impact on the program running track. However, the reduction in the amount of code will make possible future program modifications much easier.

/**
* @startTime 2020-12-17 22:00
* @endTime 2020-12-17 22:50
* @startPage 56 
* @endPage 64
* @efficiency 64/2 = 32页/天
* @needDays 412/32 = 13天
* @overDay 2020-12-16 + 13天 = 2020-12-29 
*/

2. Refactoring makes the software easier to understand

Make it easier for reviewers to understand, and make it easier for the next person to take over your code. Maybe the next person to take over your code is yourself. Sometimes, if you take a piece of code, you don’t know if you wrote it yourself, right?

  • Benefits of refactoring
  • Refactoring to help find bugs
  • Refactoring to improve programming speed

3. Timing of reconstruction

  • Three things, three reconstructions
  • Refactor when adding features
  • Refactor when fixing errors
  • Refactor when reviewing code

4. Why refactoring is useful

  1. Programs that are difficult to read and difficult to modify;
  2. Logically repeated procedures, difficult to modify;
  3. When adding a new behavior, it is necessary to modify the program of the existing code, which is difficult to modify;
  4. Programs with complex conditional logic are difficult to modify;

Therefore, we hope that the program:

  1. Easy to read
  2. All logic is only specified in a unique location;
  3. The new changes will not endanger existing behavior;
  4. Express conditional logic as simple as possible;

Indirect layer and refactoring

The value of the indirect layer:

  1. Allow logic sharing;
  2. Separate explanation of intention and realization;
  3. Isolate changes;
  4. Encapsulate conditional logic;

5. The problem of reconstruction

(1) Database

data migration

(2) Modify the interface

Don't release the interface too early, please modify your code ownership policy to make the refactoring smoother.

(3) Design changes that are difficult to complete through refactoring

(4) When should not refactor 

/**
* @startTime 2020-12-20 11:50
* @endTime 2020-12-20 15:30
* @startPage 65 
* @endPage 102
* @efficiency 102/5 = 20.4页/天
* @needDays 412/20.4 = 20天
* @overDay 2020-12-16 + 20天 =  2020-01-04
*/

6. Refactoring and design

Many people regard design as a key part of software development, and regard programming as only mechanical low-level labor. They think design is like drawing engineering drawings and coding is like construction.

Even if you fully understand the system, please actually measure its performance instead of guessing. Conjecture will make you learn something, but you are wrong in all likelihood.

7. Refactoring and performance

First write the adjustable software, then adjust it to get enough speed.

Three ways to write fast software:

(1) Time budget method

Usually used in real-time systems with extremely high performance requirements.

When decomposing the design, it is necessary to make a budget, and allocate certain resources to each component in advance, including time and execution trajectory. Each component must not exceed its own budget, even if it has a mechanism for scheduling and provisioning time between components.

This method attaches great importance to performance and is necessary for systems such as heart rate regulators, because late data in such systems is wrong. But for some management systems, such pursuit of performance is a bit too much.

(2) Continuous attention method

This method requires any programmer to try to ensure the high performance of the system when doing anything at any time.

This method looks very common and feels very attractive, but it usually doesn't help much.

(3) Performance optimization stage

When writing a program, you don't need to pay too much attention to performance. After the code is developed, there will be a performance optimization stage. Once this stage is entered, the performance of the program is adjusted according to a specific program.

In the performance optimization stage, you should first use a measurement tool to monitor the operation of the program, let it tell you where in the program a lot of time and space are consumed. In this way, a small piece of code where the performance hot spot is located can be found. Then you should focus on these hot codes and use the optimization methods in the continuous attention method to optimize them. Every step needs to be compiled, tested, and measured again.

8. Where is the origin of reconstruction?

Chapter 3 Bad Smell of Code

1. Duplicate code

When two functions in the same class contain the same expression, it is necessary to extract the repeated code, and then let both locations call the extracted piece of code.

2. Too long function

All the benefits brought by the indirect layer, the ability to explain, share, and choose.

Functions should be decomposed more aggressively. Whenever we feel that we need comments to explain something, we write the things that need to be explained into an independent function and name it after its purpose (rather than implementation). We can even compare one Group even one line of code to do this. Even if the replaced function call action is longer than the function itself, as long as the function name can explain its purpose, we should not hesitate to do so. The key is not the length of the function, but the semantic distance between the "what" and "how" of the function.

(1) Refine function

(2) Replace temporary variables with queries

(3) Introduce the parameter object or keep the object intact

(4) Replace functions with function objects

Put this special function into a separate object, so that the local variable becomes a field in the object, and then you can decompose this large function into multiple small functions in the same object.

This book continues to emphasize the beauty of small functions.

(5) Conditional expressions and loops are often also refined signals.

Decomposition conditional expression;

Distill the loop and its code into an independent function.

3. Oversized category

If you want to do too many things with a single class, too many instance variables will appear in it. Once this happens, repeated code will follow.

Several variables can be extracted together into a new class using the refined class.

4. Too long parameter column

5. Divergent changes

6. Shot modification

If you encounter a certain change and you have to make small changes in many different classes, you can distill the code that has changed together into a new class.

7. Attachment plot

The function's interest in a certain class is much more interested in the class you are in. At this time, you need to transfer the position of the function.

8. Data mud group

A good judgment method is: When deleting one of the many data, does the other data lose its meaning? If they no longer make sense, this is a clear signal and you should generate a new object.

Reducing the number of fields and parameters can of course remove some bad smells, but more importantly: once you have a new object, you have the opportunity to make the program emit a fragrance. Once you have the new object, you can start looking for attachment plots, which can help you point out the various program behaviors that can be moved to the new category. It doesn't take long, all the classes will give full play to their value in their small society.

9. Basic types of paranoia

Novices in object technology are usually unwilling to use small objects in small tasks, such as special strings such as money, phone numbers, and postal codes that combine numeric values ​​and currencies. It seems simple. At this time, objects can be used to replace data values. Replace the data value that originally existed alone with the object, so as to get out of the traditional cave and enter the hot object world.

10. Switch horror appeared

One of the most obvious characteristics of object-oriented programs is that they use less switch statements.

In essence, the problem with switch statements is repetition. You will often find the same switch statement scattered in different locations. If you want to add a new case clause to it, you must find all switch statements and modify them. Polymorphism in object-oriented can bring elegant solutions.

11. Parallel inheritance system

If you find that a class adds a subclass, you must also add a subclass to another class. If you find that the class name prefix of one inheritance system is exactly the same as the name prefix of another inheritance system, you smell this bad smell.

The general strategy to eliminate this duplication is to let an instance of an inheritance system reference an instance of another inheritance system. If you make persistent efforts to use the transfer method and transfer field, you can hide the inheritance system of the reference side invisible.

12. Redundancy

Collapse the inheritance system;

Inline the class;

13. Talk about the future

14. Confusing temporary fields

15. Multi-degree coupled message chain

16. The middleman

17. Ambiguous relationship

18, similar to the same class

19.Imperfect class library

The introduction of additional functions, that is, the encapsulation of complex parameters;

Introduce local extension, that is, inherit the class library and add the methods you need;

20, naive data class

21. Rejected gift

Replace inheritance with delegation.

22, too many comments

When you see a bunch of comments, it is often because your code is bad and you need to use comments to explain it. So too many comments indicate that you should refactor the code.

When you feel the need to write comments, please try to refactor first, and try to make all comments redundant.

Chapter 4 Building a Test System

1. The value of self-testing code

In fact, the most useful time to write test code is before programming. When you need to add features, write the corresponding test code first.

Writing test code is actually asking yourself what you need to do to add this feature. Writing test code also allows you to focus on the interface rather than the implementation.

2. JUnit test framework

3. Add more tests

  • It's better to write an imperfect test and actually run it, rather than waiting for a perfect test;
  • Consider the boundary conditions that may go wrong, and focus the test firepower there;
  • When things are thought to go wrong, don’t forget to check if the expected exception is thrown;
  • Don’t write tests just because the tests cannot catch all bugs, because the tests can indeed catch most bugs;

 

Previous post: [Writing high-quality code: 151 suggestions for improving Java programs]

Next article: In- depth understanding of the Java virtual machine

Guess you like

Origin blog.csdn.net/guorui_java/article/details/111305499