私が取り組んでいるコードをlintするたびに、This function's cyclomatic complexity is too high. (7)
を取得します。しかし、私はそれがうまくいくようにそれをどのように書き直すことができるかについて少し混乱しています。
これは、そのメッセージをスローし続ける関数です。
function () {
var duration = +new Date() - start.time,
isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2,
direction = delta.x < 0;
if (!isScrolling) {
if (isPastHalf) {
if (direction) {
this.close();
} else {
if (this.content.getBoundingClientRect().left > viewport / 2 && pulled === true) {
this.close();
return;
}
this.open();
}
} else {
if (this.content.getBoundingClientRect().left > viewport / 2) {
if (this.isEmpty(delta) || delta.x > 0) {
this.close();
return;
}
this.open();
return;
}
this.close();
}
}
}
このような状況を回避するために、コードをこのように構造化する方法についてアドバイスをお願いします。
コードには2つのアクションしかありませんが、条件が多すぎます。単一のif-else-statementとブール演算子を条件で使用します。それが不可能な場合は、少なくとも
簡略化した関数は次のとおりです。
var duration = +new Date() - start.time,
isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2,
isFarRight = this.content.getBoundingClientRect().left > viewport / 2,
direction = delta.x < 0;
if (!isScrolling) {
if (isPastHalf) {
if (direction)
this.close();
else {
if (isFarRight && pulled)
this.close();
else
this.open();
}
} else {
if (isFarRight) {
// Looks like the opposite of `direction`, is it?
if (this.isEmpty(delta) || delta.x > 0)
this.close();
else
this.open();
} else
this.close();
}
}
および短縮:
var duration = +new Date() - start.time,
isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2,
isFarRight = this.content.getBoundingClientRect().left > viewport / 2,
direction = delta.x < 0,
undirection = this.isEmpty(delta) || delta.x > 0;
if (!isScrolling) {
if ( isPastHalf && ! direction && !(isFarRight && pulled)
|| !isPastHalf && !undirection && isFarRight )
this.open();
else
this.close();
}
まず、関数の結果は3つあります。何もしない、this.close()
を呼び出す、またはthis.open()
を呼び出します。したがって、理想的には、結果の関数には、使用する結果を決定するifステートメントが1つだけ含まれます。
次のステップは、すべてのブールコードを変数に抽出することです。例:var leftPastCenter = this.content.getBoundingClientRect().left > viewport / 2
。
最後に、ブール論理を使用して、段階的に単純化します。
ここに私がそれをした方法があります:
まず、すべてのブール変数を抽出します。
_function () {
var duration = +new Date() - start.time,
isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2,
direction = delta.x < 0,
leftPastCenter = this.content.getBoundingClientRect().left > viewport / 2,
positiveDelta = this.isEmpty(delta) || delta.x > 0,
isPulled = pulled === true; // I'll assume the test is needed rather than just using pulled.
if (!isScrolling) {
if (isPastHalf) {
if (direction) {
this.close();
} else {
if (leftPastCenter && isPulled) {
this.close();
return;
}
this.open();
}
} else {
if (leftPastCenter) {
if (positiveDelta) {
this.close();
return;
}
this.open();
return;
}
this.close();
}
}
}
_
引き出す最も簡単な部分は、isScrolling
がtrueの場合、何も起こらないことを認識することです。これにより、1レベルの入れ子がすぐに取り除かれます。
_ // above same
if (isScrolling) { return; }
if (isPastHalf) {
if (direction) {
this.close();
} else {
if (leftPastCenter && isPulled) {
this.close();
return;
}
this.open();
}
} else {
if (leftPastCenter) {
if (positiveDelta) {
this.close();
return;
}
this.open();
return;
}
this.close();
}
}
_
次に、this.open()
が呼び出されたケースを確認します。 isPastHalf
がtrueの場合、this.open()
は、_!direction
_および!(leftPastCenter && isPulled)
の場合にのみ呼び出されます。 isPastHalf
がfalseの場合、leftPastCenter
および_!positiveDelta
_の場合にのみthis.open()
が呼び出されます。
_ // above same
if (isScrolling) { return; }
if (isPastHalf) {
if (!direction && !(leftPastCenter && isPulled)) {
this.open();
} else {
this.close();
}
} else {
if (leftPastCenter && !positiveDelta) {
this.open();
} else {
this.close();
}
}
_
Ifsを反転する(つまりthis.close()
が最初に来る)と、コードが少しすっきりして、私の最終バージョンが得られます。
_ function () {
var duration = +new Date() - start.time,
isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2,
direction = delta.x < 0,
leftPastCenter = this.content.getBoundingClientRect().left > viewport / 2,
positiveDelta = this.isEmpty(delta) || delta.x > 0,
isPulled = pulled === true; // I'll assume the test is needed rather than just using pulled.
if (isScrolling) { return; }
if (isPastHalf) {
if (direction || (leftPastCenter && isPulled)) {
this.close();
} else {
this.open();
}
} else {
if (!leftPastCenter || positiveDelta) {
this.close();
} else {
this.open();
}
}
}
_
あなたのコードベースを知らずに、私がもっと多くをするのは難しいです。注意すべきことの1つは、direction
と私の新しい変数positiveDelta
がほぼ同じであることです。positiveDelta
を削除して、direction
を使用することもできます。また、direction
はブール値に適した名前ではありません。movingLeft
のような名前の方が適しています。
実際、これらすべてのreturn
ステートメントは問題を混乱させますが、解決策のヒントを提供します。
if (direction) {
this.close();
} else {
if (this.content.getBoundingClientRect().left > viewport / 2 && pulled === true) {
this.close();
return; // We'll never `this.open()` if this is true anyway, so combine the booleans.
}
this.open();
}
どうですか:
if (direction || (this.content.getBoundingClientRect().left > viewport / 2 && pulled === true)) {
this.close();
} else {
this.open();
}
そして:
if (this.content.getBoundingClientRect().left > viewport / 2) {
if (this.isEmpty(delta) || delta.x > 0) {
this.close();
return; // Combine the booleans!
}
this.open();
return;
}
簡素化する:
if ((this.isEmpty(delta) || delta.x > 0) || !this.content.getBoundingClientRect().left > viewport / 2) {
this.close();
} else {
this.open();
}
(脇に:元の投稿は閉じ括弧を省略しました。関数(OP)が投稿を超えて継続することを意図している場合、この答えは間違っています(ただし、それをより明確にする必要があります))
結果:2つの(繰り返される)決定を排除しました。
function () {
var duration = +new Date() - start.time,
isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2,
direction = delta.x < 0;
if (!isScrolling) {
if (isPastHalf) {
if (direction || (this.content.getBoundingClientRect().left > viewport / 2 && pulled === true)) {
this.close();
} else {
this.open();
}
} else {
if ((this.isEmpty(delta) || delta.x > 0) || !this.content.getBoundingClientRect().left > viewport / 2) {
this.close();
} else {
this.open();
}
}
}
}
ベルギはすでに正しい答えを出していますが、それでも私の好みには複雑すぎます。 fortran77を使用 ではないため、 early return を使用した方がよいと思います。また、追加の変数を導入することにより、コードをさらに明確にすることができます。
function doSomething(isScrolling, start, delta, viewport) {
if (isScrolling) return;
var duration = +new Date() - start.time;
var isPastHalf = Number(duration) < 250 && Math.abs(delta.x) > 20 || Math.abs(delta.x) > viewport / 2;
var isFarRight = this.content.getBoundingClientRect().left > viewport / 2;
// I'm not sure if my variable names reflect the actual case, but that's
// exactly the point. By choosing the correct variable names for this,
// anybody reading the code can immediatly comprehend what's happening.
var isMovingToLeft = delta.x < 0;
var isMovedPastEnd = isPastHalf && !isMovingToLeft && !(isFarRight && pulled);
var isMovedBeforeStart = !isPastHalf && isMovingToLeft && isFarRight;
if (isMovedPastEnd || isMovedBeforeStart) {
this.open();
else
this.close();
}
}