Examples of refactoring in several business scenarios


Request order dependency



In this scenario, it is the business complexity that drives the code complexity in the first place. First let’s take a look at a simple example of both front-end and Node possibilities:


We have four functions (A, B, C, and D) that request data.

C depends on the result of B, D depends on the result of ABC, and finally outputs the result of D.


Examples of errors:


/ / pseudo codefunction A(callbak) {
  ajax(url, function(res) { callbak(res); }); } / /... The rest is as follows: A(function(resa) {
  B(function(resb) {
    C(resb, function(resc) {
      D(resa, resb, resc, function(resd) {
        console.log("this is D result:", resd);
      });
    });
  });
});Copy the code


Review: emm… Although this code is written intentionally, it has been seen in some beginners.

This code still gives the correct result, but it’s ugly, callback hell. If the next person does not refactor and there are request dependencies, you have to continue calling back to nesting. Poor performance, not considering that A and B can actually be concurrent.


Here’s a look at the original callback… ES2015+ callback (async.js) –> Promise –> Generator + co –> async + await. In fact, it is from the native syntactic level to simplify and enhance our ability to control asynchronous.


Here is the final solution directly provided natively for the current phase: Based on Promise + async/await


function A() {
  return new Promise(r =>
    setTimeout(() => {
      r("a"); }, 2000)); } / /... Same as asyncfunction asyncBC() {
  const resb = await B();
  const resc = await c(resb);
  return { resb, resc };
}
async function asyncTask() {
  const [resa, { resb, resc }] = await Promise.all([A(), asyncBC()]);
  const resd = await D(resa, resb, resc);
  return resd;
}
asyncTask().then(resd => {
  console.log("this is D result:", resd);
});Copy the code


Here we are rethinking the above problem to clarify the dependence of logical order. And use the latest grammar.


Use Promise. All with async/await form, consider concurrent and serial, write succinctly, achieve the fastest scheme under the requirements of the example. Fixed infinite nesting.


This is the optimization that follows the evolution of language itself.


But it’s not just that. We categorize the problem so that B and C have sequentially dependent requests, and separate them out into separate functions. Let them deal with their own logic. We’ll come back to that later.


Torture if else


There may be some problems


  1. Excessive nesting
  2. Logical processing redundancy
  3. Poor defense programming (error handling)


This is a way to get the background color, but as the business changes, the background color comes from more and more sources, and some business people may process it like this:


const _getPageBgColor = (pageInfo, pageUrlObj) => {
  let bgColor = "";
  if (window.__isMyCache) {
    if (pageInfo && pageInfo.theme) {
      bgColor = pageInfo.theme.backgroundColor;
    } else {
      if (window.__customMyConfig.backgroundMap) {
        let queryParam = pageUrlObj.params;
        if (queryParam.myPid) {
          let pids = queryParam.myPid.split("-");
          if (pids.length === 2) {
            bgColor = window.__customMyConfig.backgroundMap[pids[1]];
          } else {
            bgColor = window.__customMyConfig.backgroundMap[pids[0]]; }}}if (!bgColor && window.__customMyConfig.customBgMap) {
        Object.keys(window.__customMyConfig.customBgMap).forEach(item => {
          if (this.pageUrl.indexOf(item) > 0) {
            bgColor = window.__customMyConfig.customBgMap[item]; }}); }}}else {
    if (window.__pageTheme) {
      bgColor = window.__pageTheme.backgroundColor; }}return bgColor;
};Copy the code


Reading the code above is a pain in the neck, and it’s almost impossible to know at a glance which branch you’re going to end up in. So here are two principles


  • Reasonable extraction into a function
  • Error priority return


There is a basic version of refactoring:


const getBackgroundMapBgColor = pageUrlObj => {
  if(! window.__customMyConfig.backgroundMap)return "";
  let queryParam = pageUrlObj.params;
  if(! queryParam.myPid)return "";
  const pids = queryParam.myPid.split("-");
  if (pids.length === 2) {
    return window.__customMyConfig.backgroundMap[pids[1]];
  }
  return window.__customMyConfig.backgroundMap[pids[0]];
};
const getCustomBgMapBgColor = () => {
  let bgColor = "";
  if (window.__customMyConfig.customBgMap) return "";
  Object.keys(window.__customMyConfig.customBgMap).forEach(item => {
    if(this.pageUrl.indexOf(item) ! = = 1) {bgColor = window.__customMyConfig.customBgMap[item]; }});return bgColor;
};
const _getPageBgColor = (pageInfo, pageUrlObj) => {
  if(! window.__isMyCache && ! window.__pageTheme)return "";
  if (window.__pageTheme) {
    return window.__pageTheme.backgroundColor;
  }
  if (pageInfo && pageInfo.theme) {
    return pageInfo.theme.backgroundColor;
  }
  let bgColor = getBackgroundMapBgColor(pageUrlObj);
  if (!bgColor) bgColor = getCustomBgMapBgColor();
  return bgColor;
};Copy the code


You can see that the whole logic has been reworked. It is split into three functions, with sub-methods handling the corresponding level of logic, and a master method responsible for scheduling. The whole thing becomes clear.


Of course, after we refactor based on the principles above, is there anything wrong with the code? B: of course.


You can see that all three of our functions depend on global variables. The function itself is not pure. If it is a global problem, it is not easy to troubleshoot.


It needs to be modified to a pure function to be easy to understand and test.


Example: We changed the global variable into a parameter, so we just passed the global variable in when we called it, but we got a pure function.


const getBackgroundMapBgColor = (pageUrlObj, config) => {
  if(! config.backgroundMap)return "";
  let queryParam = pageUrlObj.params;
  if(! queryParam.myPid)return "";
  const pids = queryParam.myPid.split("-");
  if (pids.length === 2) {
    return config.backgroundMap[pids[1]];
  }
  return config.backgroundMap[pids[0]];
};Copy the code


Now, why do I emphasize this point here, but one of the most fundamental problems in functional programming is pure functions. Only in this way can input and output be observed, and each input must have an output. And only in this way can we make the system less and less non-pure. Make your code easier to test.


Of course, if we think in terms of refactoring, we also need to pay attention to this point:


Object.keys(window.__customMyConfig.customBgMap).forEach(item => {
    if(this.pageUrl.indexOf(item) ! = = 1) {bgColor = window.__customMyConfig.customBgMap[item]; }});Copy the code


The logic here sets the last data to be matched to bgColor. (We all know that find indexOf and so on are basically previous matches.) Is it really a business requirement?


As you can see, the process of writing/refactoring business code is another step up in business logic and business understanding. Whether abstracted as a function or an error-first return design, this solves the problem of being able to understand the detailed logic of an area without having to read the whole picture, making the code easy to understand and modify.


. Even after such refactoring, there is still room for further optimization, such as the naming of functions and parameters, complete test cases, etc. Due to the length of this article, I will not expand the explanation.


Other problems that may exist in some code


  1. The logical coupling is at the view level. :a === 'a' && b ==='b' && c==='c' && d ==='d'? <div>... </div>:null
  2. Component reuse, function reuse, no encapsulation, code duplication.
  3. There is no single function. One function handles too many responsibilities. These responsibilities are unrelated, but are all coupled within the same block.
  4. Argument lists are cluttering up, there is defensive programming, and no handling of errors (interface errors, timeouts, double commits, etc.)
  5. Magic number, magic string, and not stated.
  6. Bad data structure/bad naming (the concrete code example above also exists)


Thinking about optimizing your code


First of all, why do you say you need to optimize your code?


  1. Technical pursuit.
  2. The company requires an online system. There are users in use, do not write a good problem, in fact, bitter or their own.
  3. Teamwork, I do not write well, team members and other people do not write well, the vicious cycle of suffering or their own.
  4. Iterate quickly. The system needs to constantly add new functions. You have to write good code to do that.
  5. Other people’s opinions, afraid that others will think their technical ability is poor… xxxx….


Then there are the following requirements:


  • Easy to understand the architecture of the system
  • Easy to understand system life cycle and execution process
  • It’s easy to understand what each function does
  • Easy to understand how functions are called and passed between (input and output)
  • Easy to understand the meaning of variables, the meaning of expressions.
  • Easy to expand…


At the end of the day, it actually comes back to writing clean code that is easy to understand/modify/test.

(In fact, most of the time, there is a hidden condition of staff collaboration in it, so it is necessary to write good code, but not too encapsulated, so that other members of the team can not understand (of course, if some people do not have enough experience, then it is his own problem, he needs to strengthen.) )


Some advice


  1. Understand the business more clearly and think about possible changes. Think and design clearly before you start.
  2. Take a look at open source projects and industry best practices to see what is good code and what is bad code.
  3. Although the establishment of clear code is for the computer to run, but ultimately still human reading. It’s not just the bug-free mental model.
  4. Model the thinking that business is as important as code quality. Avoid code that has to be written this way because of time.
  5. Understand that code review itself may be able to find and point out some problems, but the final implementation depends on our own, not into a form, but into our own thinking.
  6. Use error first. As much as possible, let the error return first, so that you get clean code later. (Not only forward and reverse judgments need to be considered when writing code.)
  7. Reasonably split up into separate functions. Specify input/output, error handling, and so on within a function. (For example, there is a lot of logical judgment in some scenarios, and the first thing to think about is whether the statements inside the judgment can be categorized and split out)
  8. For the judgment and combination of multiple states, the combined state table (MAP table) state machine and other modes can be used
  9. Learn about design patterns and refactoring.
  10. Refactoring!!!!! As soon as you think there’s something wrong with this place, don’t wait until later. Later is often never.


The end of the


It can feel like it’s coming to a screeching halt. In this article, we first take two specific examples of optimized code as an introduction, so that we understand some business code optimization ideas. After listing some other possible errors, as well as the ideological preparation and theoretical guidance for optimizing code. In fact, we hope that people can find problems in the business, and then think about how to solve the problem, because so much said, in the end can not write good code. Still have to


On your own.