Writing in the front

This is the fifth article in the Refactoring, Tasteful Code series, which focuses on how to create, remove, or rename elements of a program. Of course, not only these techniques, but also type refactoring is important, mainly moving elements between different contexts. You can move functions between classes and other modules by moving functions, as well as moving fields, and others will be covered in this article…

Previously on:

  • Refactoring, Tasteful Code 01 — On the Road to Refactoring
  • Refactoring, Tasteful Code 02 — Building test Systems
  • Refactoring, Tasteful Code 03 — Common Refactoring Techniques
  • Refactoring, Tasteful Code 04 — Encapsulation

Common methods of moving characteristics

In normal development, we often use the move feature in the code, but do not know what move feature is done, now we will commonly used move feature methods summarized as follows:

  • Move the function
  • Move the field
  • Move statements to functions
  • Move statements to users
  • Replace inline code with function calls
  • Mobile statement
  • Break up the cycle
  • Replace circulation with pipes
  • Remove dead code

1. Shift function

Modularity ensures that the connections between our blocks of code are easy to find, intuitive to understand, and that interrelated software elements are kept together for easy understanding and management. At the same time, as you learn more about the code and how those software elements are best organized, you need to re-modularize them by constantly moving them around.

Functions live in a context that may be global or provided by the current module. Classes are the main modularization tool, serving as the context for functions, and by nesting functions, outer functions can provide context for inner functions. In short, modules can provide a living context for functions.

Because some codes frequently refer to elements in other contexts, that is, they are closely related to elements in other contexts, and pay little attention to elements in their own contexts, they can be considered to summarize closely related elements to achieve better encapsulation effect. You can move the function if:

  • Some piece of code needs to call some other function frequently
  • Helper functions defined inside a function are called elsewhere
  • Define functions in a class

Typically, all program elements (including variables and functions) referenced by the function in the current context are checked first to see if they need to be moved, and to see if the move function is polymorphic. Copy the function into the target context, adjusting the function to fit the new context. Perform a static check to try to reference the target function correctly from the source context and modify the source function to make it a pure delegate function.

Original code:

class Account{
  constructor(){... }get bankCharge() {let result = 4.5;
    if(this._daysOverdrawn> 0) result += this.overdraftCharge;
  }
  
  get overdraftCharge() {if(this.type.isPremium){
      const basecharge = 10;if(this.dayOverdrawn <= 7) {return baseCharge;
      }else{
        return baseCharge + (this.daysOverdrawn - 7) * 0.85; }}else{
      return this.daysOverdrawn * 1.75; }}}Copy the code

Refactoring code:

class Account{
  constructor(){... }get bankcharge() {let result = 4.5;
    if(this._daysOverdrawn> 0) result += this.overdraftCharge;
  }
  get overdraftCharge() {return this.Type.overdraftCharge(this); }}class AccountType{
  constructor(){... }overdraftCharge(account){
    if(this.isPremium){
      const basecharge = 10;if(account.dayOverdrawn <= 7) {return baseCharge;
      }else{
        return baseCharge + (account.daysOverdrawn - 7) * 0.85; }}else{
      return account.daysOverdrawn * 1.75; }}}Copy the code

2. Move field

Do you have bad code in development, bad data structures, code that doesn’t have clear logic, more entanglements, and a lot of confusing useless code? Therefore, you can usually do some upfront design to try to get the most appropriate data structures, and having experience and knowledge of driving design will help you design data structures.

Of course, even experienced and skilled people will make mistakes when designing data structures, but with a deeper understanding of the problem and familiarity with business logic, they will take it into deeper and more comprehensive consideration. When the data structure is found to be unsuitable, it needs to be repaired in time. If flaws are allowed, the code will become complicated and problems will accumulate.

Every time you call a function, you need to pass in a parameter with another parameter. If you change one record, you need to change another record, which means that the field is in the wrong place. Also, if you update a field and need to make changes in multiple structures at the same time, that means you need to move the field correctly.

Specifically, to ensure that the source fields are well encapsulated, create fields (and corresponding access functions) on the target object and perform static checks to ensure that the target object is properly referenced in the source object. That is, adjust the access functions of the source object to use the fields of the target object. Finally, remove the fields on the source object.

Original code:

class User{
  constructor(name,age,getName){
    this._getName = getName;
    this._age = age;
    this._name = name;
  }
  get getName() {return this._getName; }}class UserType{
  constructor(firstName){
    this._firstName = firstName; }}Copy the code

Refactoring code:

class User{
  constructor(age,name){
    this._age = age;
    this._name = name;
  }
  get getName() {return this._name.getName; }}class UserType{
  constructor(firstName,getName){
    this._firstName = firstName;
    this._getName = getName;
  }
  get getName() {return this._getName; }}Copy the code

3. Move statements to functions

There are a few golden rules when it comes to refactoring code, the most important of which is to “eliminate duplicate” code, abstracting duplicate statements into functions, and making complex code run by calling functions.

4. Move statement to caller

The charge of the brick programmer is to design programs with uniform structure and appropriate abstraction, and functions are the magic weapon of abstraction. Of course, all means are not universally applicable. With the evolution of system capability, the abstract boundary of the original design gradually spreads outward and becomes fuzzy, and it divides into multiple different concerns from the original single whole and focusing on a single point.

The function boundary is offset, which means that the behavior that was previously called in multiple places now needs to behave differently at different points. In this way, we can remove the different behavior from the function and move it to the call.

printHtml(outData,onData.html);

function printHtml(outData,html){
  outData.write(`<p>title:${html.title}</p>`);
  outData.write(`<p>content:${html.content}</p>`);
}

Copy the code

That is:

printHtml(outData,onData.html);
outData.write(`<p>content:${onData.html.content}</p>`);

function printHtml(outData,html){
  outData.write(`<p>title:${html.title}</p>`);
  
}
Copy the code

5. Replace inline code with function calls

Functions can be used to package related behaviors, improve the expressiveness of code, clearly explain the purpose and function of code, and help eliminate duplicate code. If a piece of inline code duplicates an existing function, you can use a function call to replace the inline code and achieve abstraction of the business logic.

let flag = false;
for(const color of colors){
  if(color === "yellow") flag = true;
}
Copy the code

That is:

let flag = colors.includes("yellow");
Copy the code

6. Move statements

If you have several lines of code that use the same data structure, you can use them in relation to each other, making the code easier to understand rather than sandwiched between other data structures. So after we write the code, we need to review it and aggregate the code movement statements with strong correlation. Typically, move statements are used as a preemptive refactoring for other refactored code.

const pricingPlan = rePricingPlan();
const order = reOrder();
let charge;
const chargePerUnit = ricingPlan.uint;
Copy the code

Refactoring code:

const pricingPlan = rePricingPlan();
const chargePerUnit = ricingPlan.uint;
const order = reOrder();
let charge;
Copy the code

7. Break up loops

In normal development, multiple things are done in a loop to avoid excessive time complexity. Sometimes, too much code and too much logic in a single loop makes it hard for us to understand. Therefore, the loop can be split up to do one thing at a time, making it easier to read and use.

let averagePrice = 0;
let totalCount = 0;
for(const p in goods){
  averagePrice += p.price;
  totalCount += p.count;
}
averagePrice = averagePrice / totalCount;
Copy the code

Refactoring code:

let averagePrice = 0;
for(const p in goods){
  averagePrice += p.price;
}
let totalCount = 0;
for(const p in goods){
  totalCount += p.count;
}

averagePrice = averagePrice / totalCount;
Copy the code

If it seems silly, it’s clear when you read through complex code.

8. Replace circulation with pipes

In the past, array and object traversal was usually done by iterating through loops, but better language constructs — “collection pipes” — could also be used to handle iterations (maps, filters, etc.). A collection pipeline allows a set of operations, each of which is a collection, to describe the set iteration process.

Common practice: Create a new variable for the collection of participants in the loop, starting at the top of the C loop, and move each block of behavior within the loop. The set variable is replaced with a pipe operation until all actions within the loop have been moved, and the loop is deleted.

const users = [];
for(const item in arrs){
  if(item.age === 20) users.push(item.name);
}

// Refactor code
const users = arrs
.filter(item= >item.age === 20)
.map(item= >item.name);
Copy the code

9. Remove dead code

When a project is deployed in a production environment, there may be a large amount of code, resulting in a larger memory overhead. Useless code slows down the system and slows down the project process. Of course, most modern compilers automatically remove useless code, but it takes time and energy to read and understand the logic and principles of the code. When the code is no longer in use, it should be deleted immediately and rolled back through version control when you suddenly want to use it again.

if(false){
  ...
}
Copy the code

This is useless code and should be removed immediately.

summary

In this paper, the main introduction of moving fields, moving functions and other moving means, there are also separate statements to move, adjust the order, can also adjust the position of the code, to split the cycle, the use of pipeline replacement and other methods.

Refer to the article

Refactoring — Improving the Design of Existing Code (2nd edition)

Write in the last

Thank you for reading, I will continue to share with you more excellent articles, this article reference a large number of books and articles, if there are mistakes and mistakes, hope to correct.

More latest articles please pay attention to the author nuggets a sichuan firefly account and the public number front gravitation.