Books resources

www.aliyundrive.com/s/LoXwyqW78…

Refactoring – Code simplification

The example in the book is js, which is converted to Java code, meaning similar:

private static String statement(Invoice invoice, List<Play> plays) throws Exception {
    double totalAmount = 0;
    double volumeCredits = 0;
    String result = "Statement for " + invoice.getCustomer();
    String format = "";
    Map<String, Play> collect = plays.stream().collect(Collectors.toMap(Play::getPlayID, Function.identity()));
    for (Invoice.performance performance : invoice.getPerformances()) {
        Play play = collect.get(performance.getPlayID());
        double thisAmount = 0;
        switch (play.getType()) {
            case "tragedy":
                thisAmount = 40000;
                if (performance.getAudience() > 30) {
                    thisAmount += 1000 * (performance.getAudience() - 30);
                }
                break;
            case "comedy":
                thisAmount = 30000;
                if (performance.getAudience() > 20) {
                    thisAmount += 10000 + 500 * (performance.getAudience() - 20);
                }
                thisAmount += 300 * performance.getAudience();
                break;
            default:
                throw new Exception("unknown type:" + play.getType());
        }
        // add volume credits
        volumeCredits += Math.max(performance.getAudience() - 30.0);
        // add extra credit for every ten comedy attendees
        if ("comedy".equals(play.getType())) {
            volumeCredits += Math.floor(performance.getAudience() / 5);
        }
        // print line for this order
        result += "" + play.getName() + thisAmount / 100 + performance.getAudience();
        totalAmount += thisAmount;
    }
    result += "Amount owed is" + totalAmount / 100;
    result += "You earned " + volumeCredits;
    return result;
}
Copy the code
  1. Before refactoring, check that you have a solid set of tests. These tests must be self-verifying to prevent functionality from being unusable after refactoring

2. Refine the function, extract the computational logic into a method, and name it according to what it does, such as in the example of calculating the cost of a theater performance: amountFor

private static double amountFor(Invoice.performance performance, Play play) throws Exception {
    double thisAmount = 0;
    switch (play.getType()) {
        case "tragedy":
            thisAmount = 40000;
            if (performance.getAudience() > 30) {
                thisAmount += 1000 * (performance.getAudience() - 30);
            }
            break;
        case "comedy":
            thisAmount = 30000;
            if (performance.getAudience() > 20) {
                thisAmount += 10000 + 500 * (performance.getAudience() - 20);
            }
            thisAmount += 300 * performance.getAudience();
            break;
        default:
            throw new Exception("unknown type:" + play.getType());
    }
    return thisAmount;
}
Copy the code

3. Top-level scope (local variable), eliminate no necessary local variable, such as the amountFor play variable in the example can be queried out of the performance variable, define the getPlay method

private static Play getPlay(Invoice.performance performance) {
    return collect.get(performance.getPlayID());
}
Copy the code

4. The main coding style and naming style should be unified during reconstruction, so that the function can be seen at a glance

5. Inline variables, such as the play variable obtained by the method in the example, can be replaced by the getPlay method

6. Refactoring is about making small changes to your program. If you make a mistake, it’s easy to spot it

7. For some common methods that can be extracted into utility classes (util), the examples in the book are separated into files and imported to reference (js).

8. Polymorphic calculators are used to provide data. The example of play in the book represents drama

@component@requiredargsconstructor @log4j2 public class ChartFactory {' '/** **}  * @return */ public static IChartHandler getChartHandler(String plugin) { plugin = StringUtils.isEmpty(plugin) ? "base" : plugin; Class clazz = clazzMap.get(plugin); String simpleName = clazz.getSimpleName(); String first = simpleName.substring(0, 1); try { return SpringUtil.getBean(simpleName.replaceFirst(first, first.toLowerCase()) + "Handler", AbstractChartHandler.class); {the} the catch (NoSuchBeanDefinitionException e) warn (" component corresponding handler not query to, confirmed the existence of the handler, such as does not exist, please ignore." ); return null; }}Copy the code

Refactoring principle

  1. What is Refactoring: Refactoring: Improving the design of Existing Code (2nd edition) (Asynchronous Book)
Refactoring, n: An adjustment to the internal structure of software to improve its comprehensibility and reduce its modification costs without changing its observable behavior. Refactoring: Improving the design of existing code (2nd Ed.) (Asynchronous book) Refactoring (verb): Using a series of refactoring techniques to adjust the structure of software without changing its observable behavior.Copy the code
  1. Reconstruct two hats
Adding new features: When adding new features, I shouldn't change the existing code, just add new features. Refactoring: WHEN REfactoring, I can no longer add functionality and just tweak the structure of the code.Copy the code
  1. Why does refactoring
Refactoring improves software design: The internal design (or architecture) of a program can decay over time. Refactoring makes software easier to understand: Before refactoring, the code worked fine, but the structure was less than ideal, allowing the code to better express its intentions. Refactoring helps find bugs: Refactoring, I can understand the code, and immediately reflect the new understanding among code refactoring to improve coding speed: with the increase of development time, the efficiency of the function of the cumulative will gradually reduce, so through refactoring can easily find where to modify, how to modify, the possibility of introducing bugs will become smallerCopy the code
  1. When refactoring

Planned refactoring and opportunistic refactoring

There are three things you can do when you refactor: Do it the first time; The second time you do something similar, you'll be offended, but you can do it anyway. The third time you do something like this, you should refactor. Pre-refactoring: Making it easier to add new features: Before I start adding new features, I look at the existing code base and often find that my job would be much easier if I made a few tweaks to the code structure. Refactoring for understanding: Make code easier to understand: When looking at code, first understand what the code is doing. Refactoring can transfer the understanding in your mind to the code itself. By understanding the code and finding defects in the code and not doing well enough, you can write down the more complex problems first and then come back to reconstruct after completing the current task. The simpler ones can be easily reconstructedCopy the code
Planned refactorings set aside a period of time for refactorings, instead of adding features or fixing bugs, but there was no time in the project plan for refactorings, and most of the refactorings happened naturally while I was doing other things.Copy the code
# # to refactor most can for a long time in a few minutes -- - within a few hours at the most complete, some large project even need to spend a few weeks, but the benefits of refactoring Is very high, development faster, little bug, easier to read, easier to understand, even this also rarely have a special team to do the refactoring, a better strategy is to, Every time someone gets close to refactoring code, push it a little further toward the desired improvement.Copy the code
Before the launch of the project, a regular code review will be carried out. It is a good time for team members to review the code together, understand the code, find out the unreasonable code, and reconstruct the old and unreasonable code, which will be included in this iterationCopy the code

When should you not refactor

You shouldn't do refactoring without understanding how it works over and over again. Refactoring is easier than refactoringCopy the code
  1. The challenge of refactoring
Delay new feature development: The sole purpose of refactoring is to allow us to develop faster and create more value with less work. Code ownership: The reconstructed code not only affects the interior of a module, but also has a close relationship with other parts of the system, including the API and method called together. Once the modification of the self-help proposal, it will definitely destroy the user's program branch management: When code refactoring involves multiple teams and multiple people making changes to the same piece of code, good branch management is required. Basically, the team members create a branch, and after development is complete, their changes are combined into the main branch for testing: Each refactoring needs to be tested, and the test cases that were previously tested for the function points involved in the change need to be run to prevent the refactoring from breaking the function. Legacy code: Legacy code is also a major difficulty in refactoring, including non-standard naming, no comments, and inconsistent coding style, which makes it difficult to understand how the code works and makes refactoring personnel hesitate and face many difficulties. Database: database migration, changes need to be a lot of code modification, whether it is field, table name, index and other changes, corresponding to the code and query logic need to change a lot.Copy the code

7. Refactoring, architecture

Rather than guessing what mechanisms will be needed to provide flexibility in the future, building software based on current requirements, while making design quality high, requires not only code refactoring, but even architecture upgrades that better fit the architecture. In the process of software development, self-test code, continuous integration and reconfiguration have strong synergistic effect among each other.Copy the code

8. Refactoring and performance

Code refactoring can easily affect program performance, so test cases and pressure tests are needed. To ensure performance, take the following approach: time budget algorithm: this is usually only used for real-time systems with high performance requirements. If you use this approach, break down your design with a pre-calculated allocation of resources, including time and space, to each component. Continuous attention approach: This approach requires any programmer to try to keep the system high performance while doing everything at all times. Statistical approach: With this approach, I write well-constructed programs and pay no special attention to performance until I get into the performance tuning phase, which is usually late in development. During the performance tuning phase, I should first monitor the performance of my application with a metric tool that tells me where the application is consuming a lot of time and space. Then focus on these performance hotspots.Copy the code

9. Automate refactoring

Automated Refactoring relies on a tool or script (Refactoring Browser, IntelliJ IDEA, or Eclipse), and is supported to varying degrees by different tools. The refactoring tool not only needs to understand and modify the syntax tree, but also needs to know how to write the modified code back to the editor view. All in all, implementing a decent automated refactoring is a challenging programming task.Copy the code

Code bad smell

  1. Mysterious naming: Classes are typically named with a big camel, methods and local variables are named with a small camel, and uppercase underscore naming is commonly used in constants and enumerations.

2. Duplicate code: If you see the same code structure in more than one place, it’s a safe bet that your program will get better if you try to merge them into one.

3. Excessively long functions: The longer the function, the more difficult it is to understand and the number of lines of code in a method should be limited.

  1. Long parameter lists: Long parameter lists themselves are often confusing. If you can query the value of the second parameter, you can remove the second parameter.

  2. Global data: The problem with global data is that it can be modified from any corner of the code base, and no machine can detect which code has changed. Time and time again, global data causes those weird bugs.

6. Mutable data: Changes to data often result in unexpected results and hard-to-find bugs. I was updating data in one place and didn’t realize that another part of the software was expecting something completely different. At present, it is generally supported that data is changeable, so the risk can only be reduced by updating data to constraints.

  1. Shotgun surgery changes: we hope that the software is more likely to be modified, but found that adding the function of the need to change in different places to reach the purpose, it is easy to miss, that generates function has a bug, and so on divergent type change, should standardize the code structure, to modify the unified to a method, an API, in a class.

  2. Divergent change: We want software functionality to be easier to change, and if we need to change it, we want to be able to jump to a point in the system and make changes only there. The points of the system are equivalent to multiple different contexts that can support the modification of different functions, which is called divergent change.

9. Attachment complex: A function communicates with functions or data in another module more frequently than it communicates with data in its own module. This is the typical case of attachment complex, which is often called too strong coupling

  1. Data blob: It’s not uncommon to see the same three or four pieces of data in a project: the same fields in two classes, the same parameters in many function signatures. The data that is always tied together really should have their own objects.

11. Primitive type bias: It refers to the inconsistency of variable type modifications in a project: money, coordinates, scope, etc., equal state

  1. Repeated switch: Refactoring: Any switch statements should be eliminated with polymorphic substitution for conditional expressions.

  2. Loop statements: Too many loop statements lead to too many iterations, resulting in high time complexity

Redundant elements: Program elements (such as classes and functions) can add structure to code to support change, facilitate reuse, or even just provide better names, but sometimes you really don’t need that extra layer of structure.

  1. Large classes: If you try to do too much with a single class, you tend to have too many fields in it. Once that happens, duplicate code can follow.

  2. Notes: Notes can lead us to the various bad smells mentioned earlier in this chapter. Once a bad smell is found, the first step is to remove it with various reconstruction techniques.

  3. Use plug-ins to detect bad smells in projects: SonarLint is used in projects