reorganize function

A big part of refactoring is tidying up functions to wrap code more appropriately.

 

Almost all of the time, the problem stems from Long Methods (too long functions).

As a novice, I am like this. Writing code is as bad as writing essays. I step on a piece of watermelon rind and slide it wherever it goes. 200 lines of a function are considered short, and it is rarely considered to split a function.

Because they often contain too much information, the information is covered up by the intricate logic of the function and is not easy to identify.

The function is too long, in addition to logic, there must be a lot of other minor things added, such as data conversion, assembly or verification process, etc.

To deal with overly long functions, an important refactoring technique is the Extract Method, which extracts a piece of code from the original function and puts it into a separate function.

In fact, Extract Method is well understood, which is to extract a logically independent part of the overly long code into an independent function.

Inline Method is the exact opposite: replaces a function call action with the function's body. An Inline Method is required if, after many refinements, you realize that some of the extracted functions are not doing anything substantial, or if you need to backtrack to restore the original function.

In the past, when I watched C++, there was a thing called introverted functions, so I remember a little bit.

 

The biggest difficulty in Extract Method is dealing with local variables, and temporary variables are one of the main sources of difficulty. Well, I mean, is it so complicated to pull out a piece of code to consider local variables? When working with a function, I like to use Replace Temp with Query to get rid of all possible temporary variables. If a temporary variable is used in many places, I first use Split Temporary Variable to make it easier to convert.

 

But sometimes temporary variables are just too messy to replace. At this time, I need to use Replace Method with Method Object. It allows me to break out even the messiest of functions, at the cost of introducing a new class. Well, I have learned this point, and I actually use a class to reconstruct a function. This is the first time I heard that it can be done in this way.

 

Parameters pose slightly fewer problems than temporary variables, provided you don't assign to them within a function. If you have already done so, use Remove Assignments to Parameters. In fact, the problem is that you assign a value to the parameter passed to the function inside the function, which is not recommended.

 

Once the function is decomposed, I can figure out how to make it work better. Maybe I found out that there are still algorithms that could be improved to make the code cleaner. At this point I use the Substitute Algorithm to introduce a clearer algorithm.

 

The next step is to talk about various methods of reorganizing functions, let's go!

 

1. Extract Method

You have a piece of code that can be grouped together and isolated.

Put this code in a separate function and let the function name explain what the function does. This should be easy to understand, but I find it really difficult to come up with a name that's nice, elegant, and instantly relatable.

 

Here is a simple example:

void printOwing(double amount) {
    printBanner ();
    
    // print details
    System.out.println("name: " + _name);
    System.out.println("amount" + amount);
}

 

Then reorganized it to look like this:

void printOwing(double amount) {
    printBanner ();
    printDetails(amount);
}

void printDetails(double amount) {
    System.out.println("name: " + _name);
    System.out.println("amount" + amount);
}

 

motivation

 

Extract Method is one of my most used refactoring techniques. When I see a function that is too long or a piece of code that needs to be commented to make it understandable, I put that code into a separate function. "Functions that require annotations to be understood" is worth recalling.

 

There are several reasons why I like short and well-named functions.

 

1. If the granularity of each function is very small, the chance of function reuse is greater;

2. High-level functions that call these small functions read like a series of comments;

3. If the functions are fine-grained, it is easier to override the functions.

 

It makes sense, why didn't I think of that?

 

If you're used to seeing large functions before, it will take a while to get used to the style. Well, I see functions everywhere, little by little. then need

Name these small functions well before they really work, so you need to work on the function names. I would like to ask if I can use Chinese Pinyin? Also, don't think the function name is too long, as long as it can minimize the semantic distance between the function name and the function body, even if the function name is longer than the extracted code, it doesn't matter.

 

practice

 

Create a new function, name it according to the intent of the function (name it "what it does, not how it does it").

 

Copy the extracted code from the source function to the newly created target function.

 

Examine the extracted code carefully to see if it references variables that are "scoped to the source function" (including local variables and source function parameters).

 

Check if there are temporary variables "only for the snippet being refined". If there are, declare them as temporary variables in the target function.

 

Check the extracted code section to see if any of the local variable values ​​have been changed by it. If a temporary variable value is modified, see if you can process the extracted snippet as a query and assign the result to the relevant variable. If this is difficult to do, or if there is more than one variable being modified, you can't just distill this code as-is. You may need to use Split Temporary Variable first and then try to refine. Temporary variables can be eliminated using Replace Temp with Query.

 

Pass the local variables to be read in the extracted code segment as parameters to the target function.

 

After all local variables are processed, compile.

 

In the source function, replace the extracted snippet with a call to the target function.

 

compile, test

 

Example: no local variables

 

In the simplest case, Extract Method is a snap.

 

void printOwing() {
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
    
    // print banner
    System.out.println("***********************");
    System.out.println("**** Customer Owes ****");
    System.out.println("***********************");
    
    // calculate outstanding
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
    
    // print details
    System.out.println("name: " + _name);
    System.out.println("amount: " + outstanding);
}

 

We can easily distill the "print banner" code, just cut, paste, and insert a function call action.

 

void printOwing() {
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
    
    printBanner ();
    
    // calculate outstanding
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
    
    // print details
    System.out.println("name: " + _name);
    System.out.println("amount: " + outstanding);
}

void printBanner() {
    // print banner
    System.out.println("***********************");
    System.out.println("**** Customer Owes ****");
    System.out.println("***********************");
}

 

Example: Local Variables

 

If it's really as simple as the previous example, then there's nothing difficult. The difficulty lies in the handling of local variables, including parameters passed into the source function and temporary variables declared by the source function. The scope of local variables is limited to the source function, so when we use the Extract Method, we have to spend extra effort to deal with these variables. Sometimes these local variables even get in the way and cannot be refactored.

 

The simplest case of local variables is that the code being refined just reads the values ​​of these variables, and does not modify them. In this case we can simply pass them as parameters to the objective function.

 

For the following functions:

void printOwing() {
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
    
    printBanner ();
    
    // calculate outstanding
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
    
    // print details
    System.out.println("name: " + _name);
    System.out.println("amount: " + outstanding);
}

The "print details" part can be distilled into a function with one argument:

// Source function 
void printOwing() {
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
    
    printBanner ();
    
    // calculate outstanding
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
    // The target function calls 
    printDetails(outstanding);
}

// 目标函数
void pirntDetails(double outstanding) {
    // print details
    System.out.println("name: " + _name);
    System.out.println("amount: " + outstanding);
}

 

If necessary, multiple local variables can be handled this way.

 

If the local variable is an object, and the extracted code segment calls a function that modifies the object, you can do the same. You can also just pass this object as a parameter to the target function. Other action must be taken only if the code being refined actually assigns a value to a local variable.

 

Example: reassigning a local variable

 

The problem becomes more complicated if the extracted code segment assigns values ​​to local variables. Only the issue of temporary variables is discussed here. If you find that the parameters of the source function are assigned, you should immediately use Remove Assignments to Parameters.

 

Temporary variables that are assigned are also divided into two cases.

The simpler case is: this variable is only used in the extracted code segment. If so, you can move the declaration of the temporary variable into the extracted code section and extract it together.

Another situation is: the code outside the extracted code segment also uses this variable. This is divided into two cases: if the variable is not used again after the extracted code segment, you can just modify it directly in the target function; if the code after the extracted code block still uses this variable, You need to make the target function return the value of the variable after the change.

 

These different situations will now be described.

void printOwing() {
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
    
    printBanner ();
    
    // calculate outstanding
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }

    printDetails(outstanding);
}

Now distill the code block for the "calculation" part:

void printOwing() {
    printBanner ();
    double outstanding = getOutstanding();
    printDetails(outstanding);
}

double getOutstanding() {
    // calculate outstanding
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
    return outstanding;
}

 

The Enumeration variable e is only used in the extracted code segment, so i can move it entirely into the new function. The double variable outstanding is used both inside and outside the extracted code segment, so it must be returned by the new extracted function.

 

double getOutstanding() {
    // calculate outstanding
    Enumeration e = _orders.elements();
    double result = 0.0;
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        result += each.getAmount();
    }
    return result;
}

Here, a name is changed for the returned value.

 

What if more than one variable needs to be returned?

 

There are several options. The best option is usually: pick another piece of code to refine. I prefer to have each function return a value, so I arrange multiple functions to return multiple values.

 

Temporary variables tend to be numerous and even make refining difficult. In this case, I would try to reduce the temp variable first with Replace Temp with Query. If it's difficult to refine even with that, then I'd use Replace Method with Method Object, a refactoring that doesn't care how many temporary variables are in the code or how you use them.

 

 

2. Inline Method

 

The body and name of a function are equally clear and easy to understand.

 

Insert the function body at the function call site, then remove the function.

 

int getRating () {
     return (moreThanFiveLateDeliveries ())? 2: 1 ;
}
    
boolean moreThanFiveLateDeliveries() {
    return _numberOfLateDeliveries > 5;
}

 

motivation

 

Sometimes you'll come across functions whose internal code is as clear and readable as the function name. It's also possible that you refactored the function so that its content and name are just as clear. If this is the case, this function should be removed and the code in it should be used directly. Indirectness can be helpful, but unnecessary indirection is always uncomfortable.

 

Another situation where you need an Inline Method is when you have a bunch of poorly organized functions at hand. You can inline them all into one large function and distill it into a reasonably small function.

 

Inline Method is usually used when so many layers of indirection are used that all the functions in the system appear to be just simple delegates to another function, causing us to get confused between those delegates. Layers of indirection have value, but not all layers of indirection have value. You can find those layers of indirection that are useful and remove those that are not.

 

practice

 

Check the function to make sure it is not polymorphic.

If subclasses inherit this function, do not inline this function, because subclasses cannot override a function that does not exist at all.

 

Find all the callpoints of this function.

 

Replace all call sites of this function with the function body.

 

compile, test

 

Delete the definition of this function.

 

3. Inline Temp

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324651809&siteId=291194637