概要
Bridge パターンは、GoF デザインパターンの一つで、構造に関するデザインパターンです。
クラスを「抽象(何をするか)」と「実装(どうやるか)」の2つの軸に分けてそれぞれを継承で拡張できるようにします。 2つの軸を橋(Bridge)でつないで連携させることで、両軸の組み合わせ爆発を防ぎます。
ゲーム開発では、「敵キャラの種類」と「移動AIアルゴリズム」のような、直交する2つの次元が出てきたときに有効です。
構成
classDiagram
class Abstraction {
# _impl : IImplementor
+ Operation()
}
class RefinedAbstraction {
+ Operation()
}
class IImplementor {
<< interface >>
+ OperationImpl()
}
class ConcreteImplementorA
class ConcreteImplementorB
RefinedAbstraction --|> Abstraction : extends
Abstraction o-- IImplementor : bridge
ConcreteImplementorA ..|> IImplementor : implements
ConcreteImplementorB ..|> IImplementor : implements
Client ..> Abstraction : << use >>
| 要素 | 役割 |
|---|---|
| Abstraction | 抽象側の基底クラス。IImplementor への参照を持つ |
| RefinedAbstraction | 抽象側の拡張クラス |
| IImplementor | 実装側のインターフェース |
| ConcreteImplementor | 実装側の具体クラス |
実装例(C#)
クラス概要
早速実装例です。今回はC#でゲーム開発を行う場合を想定してみました。
利用ケースは、敵キャラの種類(ゴブリン・オーク) と 移動AIアルゴリズム(追跡・パトロール・ランダム) を独立して拡張できる機構です。
敵の種類が増えても、移動AIを変えずに済む。逆に移動AIを追加しても、敵クラスに手を加えなくてよいのがポイントです。
IMovementAI:移動アルゴリズムのインターフェース(IImplementor)ChaseAI・PatrolAI・RandomAI:移動アルゴリズムの具象クラス(ConcreteImplementor)Enemy:敵の抽象基底クラス(Abstraction)Goblin・Orc:敵の具体クラス(RefinedAbstraction)
ソースコード
1. 実装側インターフェース(IImplementor)
C#
/// <summary>
/// 移動AIインターフェース (IImplementor)
/// </summary>
public interface IMovementAI
{
void Move(string enemyName, UnityEngine.Vector2 currentPos);
}2. 具象実装(ConcreteImplementor)
C#
using System;
using UnityEngine;
/// <summary>プレイヤーを追いかける移動AI (Concrete Implementor A)</summary>
public class ChaseAI : IMovementAI
{
private readonly Transform _playerTransform;
public ChaseAI(Transform playerTransform)
{
_playerTransform = playerTransform;
}
public void Move(string enemyName, Vector2 currentPos)
{
var dir = ((Vector2)_playerTransform.position - currentPos).normalized;
Console.WriteLine($"[追跡] {enemyName} がプレイヤー方向 {dir} へ移動");
}
}
/// <summary>ルートを巡回するパトロールAI (Concrete Implementor B)</summary>
public class PatrolAI : IMovementAI
{
private readonly Vector2[] _waypoints;
private int _index;
public PatrolAI(Vector2[] waypoints)
{
_waypoints = waypoints;
}
public void Move(string enemyName, Vector2 currentPos)
{
var target = _waypoints[_index % _waypoints.Length];
_index++;
Console.WriteLine($"[パトロール] {enemyName} がウェイポイント {target} へ移動");
}
}
/// <summary>ランダムに動き回るAI (Concrete Implementor C)</summary>
public class RandomAI : IMovementAI
{
private readonly System.Random _rng = new();
public void Move(string enemyName, Vector2 currentPos)
{
var dir = new Vector2((float)(_rng.NextDouble() * 2 - 1),
(float)(_rng.NextDouble() * 2 - 1)).normalized;
Console.WriteLine($"[ランダム] {enemyName} が方向 {dir} へ移動");
}
}3. 抽象基底クラス(Abstraction)
C#
/// <summary>
/// 敵の抽象クラス (Abstraction)
/// IMovementAI への参照を持ち、移動処理を委譲する
/// </summary>
public abstract class Enemy
{
protected readonly IMovementAI _movementAI;
protected readonly string Name;
protected Enemy(string name, IMovementAI movementAI)
{
Name = name;
_movementAI = movementAI;
}
public abstract void Act(UnityEngine.Vector2 currentPos);
}4. 拡張抽象クラス(RefinedAbstraction)
C#
using System;
using UnityEngine;
/// <summary>ゴブリン (Refined Abstraction)</summary>
public class Goblin : Enemy
{
public Goblin(string name, IMovementAI movementAI) : base(name, movementAI) { }
public override void Act(Vector2 currentPos)
{
Console.Write("[ゴブリン] ");
_movementAI.Move(Name, currentPos);
}
}
/// <summary>オーク (Refined Abstraction)</summary>
public class Orc : Enemy
{
public Orc(string name, IMovementAI movementAI) : base(name, movementAI) { }
public override void Act(Vector2 currentPos)
{
Console.Write("[オーク] ");
_movementAI.Move(Name, currentPos);
}
}5. クライアントコード
C#
using UnityEngine;
class BridgeSample
{
static void Main()
{
var playerTransform = new GameObject("Player").transform;
var waypoints = new[] { new Vector2(0, 5), new Vector2(5, 5), new Vector2(5, 0) };
// ゴブリンはプレイヤーを追いかける
Enemy goblin = new Goblin("ゴブ太", new ChaseAI(playerTransform));
goblin.Act(new Vector2(3, 3));
// => [ゴブリン] [追跡] ゴブ太 がプレイヤー方向 (-0.7, 0.7) へ移動
// オークはルートをパトロールする
Enemy orc = new Orc("オークA", new PatrolAI(waypoints));
orc.Act(new Vector2(0, 0));
// => [オーク] [パトロール] オークA がウェイポイント (0, 5) へ移動
// ゴブリンでもパトロールさせられる(組み合わせ自由)
Enemy patrolGoblin = new Goblin("ゴブ次郎", new PatrolAI(waypoints));
patrolGoblin.Act(new Vector2(1, 1));
// => [ゴブリン] [パトロール] ゴブ次郎 がウェイポイント (0, 5) へ移動
// 実行時にAIを差し替えることも可能(インターフェースへの参照を更新するなど)
}
}新しい敵クラスや移動AIを追加しても、もう一方の軸のクラスを変更する必要がありません。
使いどころ
- 2つの独立した軸でクラスを拡張したい場合(例:敵キャラ種 × 移動AIアルゴリズム)
- 継承による組み合わせ爆発を避けたいとき
- 実装の詳細を実行時に切り替えたいとき
長所・短所
✅ 長所
- 抽象と実装を独立して拡張できる(開放閉鎖の原則)
- 継承の組み合わせ爆発を防げる
- 実行時に実装を差し替えることができる
⚠️ 短所
- クラスの分割数が増えるため、シンプルな設計では過剰になる
- 設計の理解に慣れが必要で、初見では構造が複雑に見える
他GoFパターンとの比較・関係
Adapter との違い
Adapter は「すでに存在するクラスを別インターフェースに変換する」後付け対応です。
Bridge は「設計段階から抽象と実装を分ける」意図的な設計です。
Abstract Factory との関係
AbstractFactory を使って IImplementor の具体実装を生成することがあります。
Bridge の「実装側」をどのファクトリで生成するかを切り替えられます。
Strategy との違い
Strategy は「アルゴリズム(振る舞い)」を切り替えるパターンです。
Bridge は「実装の詳細」を抽象から分離するパターンです。
まとめ
- Bridge パターンは、抽象と実装を独立した継承ツリーに分離する
- 2つの軸の組み合わせ爆発を防ぎ、独立して拡張できる
- ゲーム開発ではキャラクター種別と描画方式の分離など直交する軸に有効
- Adapter は後付け変換、Bridge は設計段階からの分離という違いがある
- Abstract Factory や Strategy と組み合わせて使われることも多い

