【デザインパターン入門】第2回:Observer パターン – イベント通知を実現する手法

デザインパターン入門-Observerパターン

デザインパターンは、プログラムを設計するときによく使われる解決策のパターンです。

本記事では、C# を用いて Observer パターンとは何かを解説し、サンプルコードを交えて使い方や利点を紹介します。

デザインパターンを学べるおすすめの本

オライリー・ジャパン出版の「Head First デザインパターン」は、デザインパターンを学びたいすべての人におすすめの一冊です。

この本は、頭の中で知識が定着するように工夫されており、視覚的な説明や独自の教育手法が盛り込まれています。これにより、デザインパターンの概念を深く理解し、楽しく分かりやすい形式でデザインパターンが学べる内容となっています。

Observer パターンとは?

Observer パターンは、あるオブジェクト(Subject)の状態が変わったときに、他のオブジェクト(Observer)にその変更を知らせる方法です。

Observer パターンは、一般的にイベント駆動型システムや、状態の変化を監視する必要がある場合に使用されます。

Observerパターン概要図

Observer パターンの実装には、主に以下の要素が含まれます。

Subject

状態が変化するオブジェクト。通常、オブザーバーを登録/削除し、状態の変化があったときにオブザーバーに通知するメソッドを持っています。

Observer

Subject の状態変化を監視するオブジェクト。Subjectから通知を受け取ったときに実行する処理を定義します。

ConcreteSubject

Subject の具体的な実装。状態を持ち、状態が変化したときにオブザーバーに通知します。

ConcreteObserver

Observer の具体的な実装。Subject から通知を受け取ったときに実行する具体的な処理を定義します。

ゲーム開発における Observer パターンの活用例

Observer パータンはどういう場面で活用できるんだろう?

ゲーム開発における Observer パターンの活用例をいくつか紹介します。

キャラクターのステータス変更

ゲーム内のキャラクターのステータス(HP、MP、経験値など)が変更された際に、UIや他のゲームオブジェクトにその変更を通知するために Observer パターンを利用できます。

この場合、キャラクターが Subject であり、UI や他のゲームオブジェクトが Observer となります。

アチーブメントシステム

ゲーム内でプレイヤーが特定の条件を達成した際にアチーブメントを解除するシステムを実装する際、Observer パターンが役立ちます。

例えば、敵を倒した数やアイテムの収集数などの条件が達成されたとき、アチーブメントシステムが通知を受け取り、達成したアチーブメントを表示することができます。

イベント駆動型のゲームシステム

ゲーム内の様々なイベントを監視し、それらが発生した際に特定のアクションをトリガーするシステムを実装する場合、Observer パターンを使用できます。

例えば、プレイヤーが特定の場所に入ったときに敵を出現させる、アイテムを拾ったときにステータスを上昇させるなど、イベントに応じたアクションを実行することができます。

マルチプレイヤーゲームの同期

オンラインマルチプレイヤーゲームにおいて、プレイヤーのアクションやゲームの状態がリアルタイムで同期される必要があります。

Observer パターンは、ゲームの状態が変更されたときに、他のプレイヤーにその変更を通知し、ゲームを同期させるために使用できます。


これらの実用例からもわかるように、ゲーム開発において Observer パターンは非常に有用であり、様々な場面で活用されています。

Observer パターンの実装例

以下に、キャラクターの HP 変更を例にした Observer パターンの C# コードを示します。

Observerパターンの実装例

まず、オブザーバーを表すインターフェースを定義します。

public interface IHealthObserver
{
    void OnHealthChanged(int newHealth);
}

次に、キャラクター(Subject)クラスを定義します。

public class Character
{
    private int _health;
    private List<IHealthObserver> _observers = new List<IHealthObserver>();

    public Character(int health)
    {
        _health = health;
    }

    public void RegisterObserver(IHealthObserver observer)
    {
        _observers.Add(observer);
    }

    public void UnregisterObserver(IHealthObserver observer)
    {
        _observers.Remove(observer);
    }

    public void ChangeHealth(int newHealth)
    {
        _health = newHealth;
        NotifyObservers();
    }

    private void NotifyObservers()
    {
        foreach (IHealthObserver observer in _observers)
        {
            observer.OnHealthChanged(_health);
        }
    }
}

HPバーのオブザーバー実装を定義します。

public class HealthBar : IHealthObserver
{
    public void OnHealthChanged(int newHealth)
    {
        // HP が変更したら通知が来る
        Console.WriteLine($"HPバー: Hpを {newHealth} に変更");
    }
}

ステータスウィンドウのオブザーバー実装を定義します。

public class StatusWindow : IHealthObserver
{
    public void OnHealthChanged(int newHealth)
    {
        // HP が変更したら通知が来る
        Console.WriteLine($"ステータスウィンドウ: Hpを {newHealth} に変更");
    }

最後に、これらのクラスを使用してキャラクターの HP を変更する例を示します。

public static void Main(string[] args)
{

    // HP が 100 のキャラクターを生成
    var character = new Character(100);
    var healthBar = new HealthBar();
    var statusWindow = new StatusWindow();

    // オブザーバーの登録
    character.RegisterObserver(healthBar);
    character.RegisterObserver(statusWindow);

    // HP の変更時に、登録しているオブザーバーに通知
    character.ChangeHealth(80);

}

出力結果

HPバー: Hpを 80 に変更
ステータスウィンドウ: Hpを 80 に変更

このコードでは、キャラクターがHPを変更すると、登録されたオブザーバー( HealthBar と StatusWindow )に通知が送られ、それぞれが表示を更新します。

このように、Observer パターンを使用することで、キャラクターのHP変更と表示の更新を疎結合にできます。

Observer パターンの利点

Observer パターンの利点は主に以下の通りです。

変更が最小限に抑えられる

Observer パターンを使用することで、Subject(観察対象)と Observer(観察者)の間の依存関係を最小限に抑えることができます。

これにより、1つのコンポーネントの変更が他のコンポーネントに影響を与えることが少なくなります。

拡張性

新しい Observer を追加する際、Subject には変更を加える必要がなく、新しい Observer クラスを実装して登録するだけで済みます。

これにより、アプリケーションの拡張性が向上します。

イベント駆動のアプリケーションの構築

Observer パターンはイベント駆動のアプリケーションを構築する際に非常に役立ちます。

Subject がイベントを発行し、登録されている Observer がイベントを受け取ることで、アプリケーションのコンポーネント間で情報を効果的に伝播させることができます。

非同期通信

Observer パターンは、非同期通信を実現する際にも役立ちます。

例えば、Subject が時間がかかる処理を行っている間に、Observer は他のタスクを実行し続けることができます。処理が完了したら、Subject が Observer に通知し、結果を受け取ることができます。

疎結合

Observer パターンを使用することで、Subject と Observer の間の結合度を低く保つことができます。

これにより、コードの可読性や保守性が向上し、コンポーネントの再利用が容易になります。


これらの利点により、Observer パターンはソフトウェア設計において広く使用されています。

特に状態の変更を監視し、複数のオブジェクトにその変更を通知する必要がある場合に適しています。

まとめ

本記事では、Observer パターンについて解説しました。Observer パターンは、オブジェクト間の依存関係を減らし、イベント通知を効率的に行うデザインパターンです。

Observer パターンの概要、構成要素、利点、そして実装例を通じて、このパターンの理解を深めました。

特に、ゲーム開発においては、Observer パターンは様々な場面で活用できます。例えば、キャラクターのステータス変更やゲームの進行状況の更新など、複数のオブジェクトが関与するイベントが発生した際に、関連するオブジェクトへの通知をスムーズに行うことができます。

Observer パターンを理解し、実践的なゲーム開発に活用することで、コードの可読性や保守性を向上させることができます。今後も他のデザインパターンを学び、より効率的で品質の高いゲーム開発を目指しましょう。

参考図書