6. ネストを回避する
いくつかの条件があって、全てクリアしたときに処理を行いたい場合があると思います。愚直に書くとすれば、if文の中にif文を書いて…というように入れ子構造的に条件分岐を行うことでしょう。
しかしこれでは条件が多ければ多いほどネストが深くなっていき、どんどん見通しが悪くなっていきます。ある条件を満たしたときにどこからどこまでが実行されるのか、あるいは満たしていない場合に何が起こるのか。それを理解するためにいたずらに時間を使ってしまいます。if文に限らず、ネストが深くなるのは極力避けるべきです。
csharp
// ❌ Bad: ネストが深い
void ApplyDamage(Character target, int damage)
{
if (target != null)
{
if (target.IsAlive)
{
if (!target.IsInvincible)
{
if (damage > 0)
{
target.HitPoint -= damage;
if (target.HitPoint <= 0)
{
target.Die();
}
}
}
}
}
}また、ネストを浅くしたいからといって、条件を全て論理演算でまとめるのも悪手です。単純に条件が長くなり可読性が低くなります。
csharp
// ❌ Bad: 条件を全てまとめると読みにくい
void ApplyDamage(Character target, int damage)
{
if (target != null && target.IsAlive && !target.IsInvincible && damage > 0)
{
target.HitPoint -= damage;
if (target.HitPoint <= 0)
{
target.Die();
}
}
}早期return
ネストを浅くする方法の一つに早期returnがあります。早期returnとは、条件を満たしていない場合に直ちにreturnで抜けるという手法です。先程の複数のif文にも早期returnを適用すると、ネストが浅くなり非常に見通しが良くなります。
更に、早期returnによって条件とロジックがそれぞれ明確に分離され、変更が非常にやりやすくなります。
csharp
// ✅ Good: 早期return
void ApplyDamage(Character target, int damage)
{
if (target == null) return;
if (!target.IsAlive) return;
if (target.IsInvincible) return;
if (damage <= 0) return;
target.HitPoint -= damage;
if (target.HitPoint <= 0)
{
target.Die();
}
}早期continue
ループ処理中の条件分岐にも早期returnの考えが適用できます。returnではなくcontinueをすることによって条件を満たさない場合に即座に次のループへ移行でき、ネストが浅くなります。
csharp
// ❌ Bad
foreach (var enemy in enemies)
{
if (enemy.IsAlive)
{
if (enemy.IsInRange(player))
{
enemy.Attack(player);
}
}
}
// ✅ Good: 早期continue
foreach (var enemy in enemies)
{
if (!enemy.IsAlive) continue;
if (!enemy.IsInRange(player)) continue;
enemy.Attack(player);
}break
早期continueと同様に、breakによってループを抜けることもできます。
csharp
// ❌ Bad
Item foundItem = null;
foreach (var item in inventory)
{
if (foundItem == null)
{
if (item.Type == ItemType.Potion)
{
if (item.Count > 0)
{
foundItem = item;
}
}
}
}
// ✅ Good: 早期continue + break
Item foundItem = null;
foreach (var item in inventory)
{
if (item.Type != ItemType.Potion) continue;
if (item.Count <= 0) continue;
foundItem = item;
break;
}