我有两个功能不那么大,也不那么复杂(也许是因为我写了它们,但目前看来还不那么),并且我尝试过(成功地)重构它们,但是,这会被认为是过大的了吗?它:
原始功能:
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);
现在显然第一件事是,当像这样重构时,这是更多的代码,并且在此处编写函数时查看函数的前后,原始的方法更具可读性,但是我将它们安排在一个之后在我的代码中按顺序排列另一个,并且它们的内部代码被“最小化”,所以我只按逻辑顺序看到函数的名称。
所以我的问题是,这会被视为我必须要做的事情吗?
如果没有,那么函数必须满足什么条件才能执行此类操作?
那是正确的方法吗?
答案 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)
恕我直言,标准为可测试性和可读性。 首先意味着该功能可以轻松测试。如果参数数量增加,则该功能的单元测试的大小也会增加。如果您的函数执行其他操作(而不是一项精确的操作),则您的测试函数也必须对其进行测试。您函数的所有控制结构都会迫使您对其进行测试。
因此,如果可以通过单元测试对容易和完全进行测试,则您的功能就足够小了。