【デザインパターン】Proxyを学ぶ【C#】

C#

一言で言うと…

  • 本物のオブジェクトへのアクセスを代理オブジェクトで制御する仕組み

概要

Proxy パターンは、GoF デザインパターンの一つで、構造に関するデザインパターンです。

本物のオブジェクト(Real Subject)への参照を持つ代理オブジェクト(Proxy)を間に挟むことで、 アクセス制御・遅延初期化・ログ記録・キャッシュなどを本物のオブジェクトを変更せずに実現できます。

ゲーム開発では、重いアセット(テクスチャ・音声ファイル)の遅延ロードに活用できます。 「アセットが実際に必要になるまでロードしない」という最適化を、クライアント側のコードを変えずに実現できます。

構成

classDiagram
    class ISubject {
        << interface >>
        + Request()
    }
    class RealSubject {
        + Request()
    }
    class Proxy {
        - _realSubject : RealSubject
        + Request()
    }
    RealSubject ..|> ISubject : implements
    Proxy ..|> ISubject : implements
    Proxy o-- RealSubject : delegates
    Client ..> ISubject : << use >>
要素役割
ISubjectRealSubject と Proxy を統一するインターフェース
RealSubject本物の処理を行うオブジェクト
ProxyRealSubject へのアクセスを制御する代理オブジェクト
ClientISubject 経由で使う(Proxy か RealSubject かを意識しない)

実装例(C#)

クラス概要

早速実装例です。今回はC#でゲーム開発を行う場合を想定してみました。

利用ケースは以下のように、重いテクスチャを遅延ロードする仮想Proxy(Virtual Proxy)の実装です。

  • ITexture:テクスチャの共通インターフェース(ISubject)
  • RealTexture:実際にファイルをロードするクラス(RealSubject)
  • TextureProxy:初回アクセス時だけロードする遅延Proxy(Proxy)

ソースコード

1. 共通インターフェース(ISubject)

C#
/// <summary>
/// テクスチャインターフェース (ISubject)
/// </summary>
public interface ITexture
{
    string Name { get; }
    void Render();
}

2. 本物のテクスチャ(RealSubject)

C#
using System;

/// <summary>
/// 実際のテクスチャ (Real Subject)
/// コンストラクタでファイルをロードする(重い処理)
/// </summary>
public class RealTexture : ITexture
{
    public string Name { get; }

    public RealTexture(string name)
    {
        Name = name;
        // ファイルロードの重い処理をシミュレート
        Console.WriteLine($"[RealTexture] '{name}' をディスクからロード中...");
    }

    public void Render()
    {
        Console.WriteLine($"[RealTexture] '{Name}' を描画");
    }
}

3. 遅延ロードProxy(Proxy)

C#
/// <summary>
/// テクスチャの遅延ロードProxy (Proxy)
/// 初回の Render() 呼び出し時にだけ RealTexture を生成する
/// </summary>
public class TextureProxy : ITexture
{
    public string Name { get; }
    private RealTexture _realTexture;  // 遅延初期化

    public TextureProxy(string name)
    {
        Name = name;
        // ここではファイルロードしない
        Console.WriteLine($"[Proxy] '{name}' のProxyを作成(まだロードしない)");
    }

    public void Render()
    {
        // 初回アクセス時だけ本物を生成(遅延ロード)
        if (_realTexture == null)
        {
            _realTexture = new RealTexture(Name);
        }
        _realTexture.Render();
    }
}

4. クライアントコード

C#
using System;

class ProxySample
{
    static void Main()
    {
        // Proxy を生成(この時点ではロードしない)
        ITexture bgTexture   = new TextureProxy("background.png");
        ITexture enemySprite = new TextureProxy("enemy_red.png");

        Console.WriteLine("\n--- ゲームスタート ---");

        // 背景を描画(初回ロードが発生)
        bgTexture.Render();
        // => [RealTexture] 'background.png' をディスクからロード中...
        // => [RealTexture] 'background.png' を描画

        // 2回目は既にロード済みのものを使う
        bgTexture.Render();
        // => [RealTexture] 'background.png' を描画

        // enemySprite はこの時点までロードされていない
        Console.WriteLine("(enemy_red.png はまだロードされていない)");
        enemySprite.Render();
        // => [RealTexture] 'enemy_red.png' をディスクからロード中...
        // => [RealTexture] 'enemy_red.png' を描画
    }
}

使いどころ

  • 重いオブジェクトを必要になるまでロードしたくないとき(Virtual Proxy)
  • オブジェクトへのアクセスを権限チェックで制御したいとき(Protection Proxy)
  • アクセスのログ記録やキャッシュを本物のクラスを変えずに追加したいとき(Logging / Caching Proxy)

長所・短所

✅ 長所

  • Real Subject を変更せずにアクセス制御・最適化ができる(開放閉鎖の原則)
  • 遅延ロードでアプリの起動パフォーマンスを改善できる
  • クライアントは Proxy の存在を意識せずに ISubject として使える

⚠️ 短所

  • クラスが増えるため、シンプルなケースでは過剰設計になる
  • Proxy の追加により、処理フローが間接的になりデバッグしにくくなることがある

他GoFパターンとの比較・関係

Adapter との違い

Adapter はインターフェースを変換します。

Proxy は同じインターフェースを保ちながら、アクセスを制御・補完します。

Decorator との違い

Decorator は機能を動的に追加することが目的です。

Proxy はアクセス制御・遅延ロードなどが目的で、機能は変えません。

パターン目的
Adapterインターフェース変換
Decorator機能追加
Proxyアクセス制御・遅延・ログ

まとめ

  • Proxy パターンは、本物のオブジェクトへのアクセスを代理オブジェクトで制御する
  • Virtual Proxy(遅延ロード)・Protection Proxy(権限制御)・Logging Proxy など用途が多い
  • ゲーム開発では重いテクスチャ・音声アセットの遅延ロードに有効
  • Adapter は変換、Decorator は機能追加、Proxy はアクセス制御という違いを押さえる
  • 同じインターフェースを保つためクライアントは Proxy の存在を意識せずに使える

タイトルとURLをコピーしました