Skip to content

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;
}

Unity設計講習会 資料公開ページ