Design Pattern / Game Programming

[ゲームプログラミング] Subclass Sandbox パターン

親クラスが安全な拡張APIを提供し、子クラスはその範囲内でのみ実装するよう制限するパターンです。

  • Design Pattern
  • Game Programming
目次

パターンの一行要約

親クラスが安全な拡張APIを提供し、子クラスはその範囲内でのみ実装するよう制限するパターンです。

Unityでの典型的な使用例

  • コアルールを保護しつつ、スキルやAIを拡張する場合。
  • 企画者向けの拡張ポイントを制御したい場合。

構成要素(役割)

  • Base Class: 安全なAPIを提供
  • Hook: 子クラスの実装ポイント
  • Subclass: 拡張実装

Unityサンプル(C#)

以下のコードは、上で説明したシナリオに基づいた簡略化されたUnityのサンプルです。

public abstract class SkillBase
{
    public void ExecuteSkill()
    {
        if (!CanExecute())
        {
            return;
        }
        ConsumeResource();
        PlayCastAnimation();
        ApplyEffect();
    }

    protected virtual bool CanExecute() => true;

    protected virtual void ConsumeResource() { }

    protected virtual void PlayCastAnimation() { }

    protected abstract void ApplyEffect();
}

public sealed class FireballSkill : SkillBase
{
    protected override void ApplyEffect()
    {
        // Spawn fireball projectile.
    }
}

利点

  • 親クラスが許可するAPIだけが公開されるため、拡張コードを安全に制約できます。
  • クールダウンやリソースコストなど、共通ルールを基底クラスで強制したいときに有効です。

注意点

  • 基底クラスが大きくなりすぎると、かえって拡張の柔軟性が低下する可能性があります。
  • フックの契約が曖昧だと、サブクラス間で動作の一貫性が崩れることがあります。

相互作用図

親のテンプレートが共通ルールを強制し、子クラスはフックのみを実装する流れを示します。

flowchart LR

  caller["SkillRunner"]
  base["SkillBase.Cast()"]
  validate["Mana / Cooldown Check"]
  hook["DoCast() Hook"]
  child["FireballSkill.DoCast"]
  common["Apply Global Rule"]
  result["Skill Executed"]

  caller --> base
  base --> validate
  validate -->|pass| hook
  hook --> child
  child --> common
  common --> result
  validate -->|fail -> cancel| result

コメント