将大功能重构为功能内的功能?

时间:2020-06-23 12:29:21

标签: javascript software-quality

我有两个功能不那么大,也不那么复杂(也许是因为我写了它们,但目前看来还不那么),并且我尝试过(成功地)重构它们,但是,这会被认为是过大的了吗?它:

原始功能:

setInterval(
  () => {
    robots.forEach((robot) => {
      ping.promise.probe(robot.IP).then(async (resp) => {
        if (resp.alive) {
          for (let dataType of TypesOfDataToGet) {
            await dataType.Set(robot);
          }
        } else {
          console.log(`${robot.Name.EN} is offline!`);
        }
      });
    });
  }, 400);

进入:

function iterateRobots(robots, doSomethingOnRobots) {
  robots.forEach((robot) => {
    doSomethingOnRobots(robot);
  });
}
function pingRobots(robot) {
  ping.promise.probe(robot.IP).then(async (resp) => {
    getRobotDataIfRobotAlive(resp, robot);
  });
}
async function getRobotDataIfRobotAlive(resp, robot) {
  if (resp.alive) {
    for (let dataType of TypesOfDataToGet) {
      await dataType.Get(robot);
    }
  } else {
    console.log(`${robot.Name.EN} is offline!`);
  }
}

setInterval(() => {
  iterateRobots(robots, pingRobots);
}, 400);

原始第二功能:

robots.forEach((robot) => {
  robot.Events.forEach((event) => {
    socket.on(event, (data) => {
      let eventStartIndex = event.lastIndexOf("-");
      let eventDataType = event.substring(eventStartIndex + 1);

      for (let currentDataType of TypesOfDataToSet) {
        if (currentDataType.DataType === eventDataType.toUpperCase()) {
          currentDataType.Set(robot, data);
          break;
        }
      }
    });
  });
});

进入:

function iterateRobots(robots, doSomethingOnRobots) {
  robots.forEach((robot) => {
    doSomethingOnRobots(robot);
  });
}
function iterateEvents(robot) {
  robot.Events.forEach((event) => {
    sendDataBasedOnEventType(robot, event)
  });
}
function sendDataBasedOnEventType(robot, event) {
  socket.on(event, (data) => {
    let eventStartIndex = event.lastIndexOf("-");
    let eventDataType = event.substring(eventStartIndex + 1);

    for (let currentDataType of TypesOfDataToSet) {
      if (currentDataType.DataType === eventDataType.toUpperCase()) {
        currentDataType.Set(robot, data);
        break;
      }
    }
  });
}
    
iterateRobots(robots, iterateEvents);

现在显然第一件事是,当像这样重构时,这是更多的代码,并且在此处编写函数时查看函数的前后,原始的方法更具可读性,但是我将它们安排在一个之后在我的代码中按顺序排列另一个,并且它们的内部代码被“最小化”,所以我只按逻辑顺序看到函数的名称。

所以我的问题是,这会被视为我必须要做的事情吗?

如果没有,那么函数必须满足什么条件才能执行此类操作?

那是正确的方法吗?

2 个答案:

答案 0 :(得分:1)

第一个技巧是利用JS中的函数是一等公民,所以这个:

  robots.forEach((robot) => {
    doSomethingOnRobots(robot)
  })

可以写为:

  robots.forEach(doSomethingOnRobots)

一些可能使重构感到尴尬的事情是,其中某些提取的函数需要使用robot作为参数,而在原始函数中,这些函数是通过闭包访问的。

第一个示例

您可以寻找以保留此闭包的方式拆分功能的方法。由于在示例中使用了async,因此您也可以将其用于第一个承诺:

async function pingRobot (robot) {
  const resp = await ping.promise.probe(robot.IP)

  if (!resp.alive) return console.log(`${robot.Name.EN} is offline!`)

  for (let dataType of TypesOfDataToGet) {
    await dataType.Set(robot)
  }
}

setInterval(() => robots.forEach(pingRobot), 400)

通过将计时器和迭代的核心逻辑(检查机器人状态)分开,我们使pingRobot函数更易于测试。

第二个例子

关于第二个函数,可能需要用允许您从事件DataType获取类型的结构替换迭代。使用keyBy(可以根据需要手动实现)的示例:

const typesByDataType = keyBy(TypesOfDataToSet, 'DataType')

function onRobotEvent ({ robot, event, data }) {
  const eventStartIndex = event.lastIndexOf("-")
  const eventDataType = event.substring(eventStartIndex + 1).toUpperCase()
  const eventType = typesByDataType[eventDataType]
  if (eventType) eventType.Set(robot, data)
}

robots.forEach(robot => {
  robot.Events.forEach(event => {
    socket.on(event, data => {
      onRobotEvent({ robot, event, data })
    })
  })
})

主要技巧是再次查看原始代码中利用了哪些闭包,并保留它们以避免冗长。尽管可能更长一些,但是onRobotEvent变得更容易推理和独立测试。

答案 1 :(得分:0)

恕我直言,标准为可测试性可读性。 首先意味着该功能可以轻松测试。如果参数数量增加,则该功能的单元测试的大小也会增加。如果您的函数执行其他操作(而不是一项精确的操作),则您的测试函数也必须对其进行测试。您函数的所有控制结构都会迫使您对其进行测试。

因此,如果可以通过单元测试对容易完全进行测试,则您的功能就足够小了。