ゲームが作れるようになるまでがんばる日記

ゲーム制作のことを中心にゲームに関することを書いています

Bridge

機能のクラス階層と実装のクラス階層を分けて、その橋渡しをする。
またゲームで考えてみる。攻撃メソッドを持った敵クラスを作成し、それを継承してゴブリンクラスを作成。

class Enemy {
public:
    void Attack();
};

class Goblin : public Enemy {
public:
    void Attack() {
        printf("Goblin Attack.\n");
    }
};

ここで敵クラスを継承してボス敵クラスを作成し、2回連続で攻撃する特殊攻撃のメソッドを追加した。

class BossEnemy : public Enemy {
public:
    void SpecialAttack() {
        Attack();
        Attack();
    }
};

ボス敵のゴブリンクラスを作成するため、ボス敵クラスを継承。

class BossGoblin : public BossEnemy {
public:
    void Attack() {
        printf("Goblin Attack.\n");
    }
};

こちらでもゴブリンの攻撃を実装しなくてはならなくなってしまう。
また、ゴブリンだけでなくスライムなどほかの種類の敵が増えたり、また攻撃種類が増えてメソッドが追加されたりしたら、そのたびにクラスを変更しなくてはならないのは大変。
そこで、Bridgeパターンを使って、機能を追加するところと実装をするところを委譲を使って分けるようにする。
実装のクラス階層のEnemyImplクラスを作成し、機能のクラス階層のEnemyクラスを定義。EnemyクラスはEnemyImplのインスタンスを持ち、そのインスタンスを使って処理を行う。つまり処理を任せるので委譲。

class EnemyImpl
{
public:
    virtual void rawAttack() = 0;
};

class Enemy {
private:
    EnemyImpl* impl;
public:
    Enemy(EnemyImpl* impl) {
        this->impl = impl;
    }
    void Attack() {
        impl->rawAttack();
    }
};

class BossEnemy : public Enemy {
public:
    BossEnemy(EnemyImpl* impl)
            : Enemy(impl) {}
    void SpecialAttack() {
        Attack();
        Attack();
    }
};

class Goblin : public EnemyImpl {
public:
    void rawAttack() {
        printf("Goblin Attack.\n");
    }
};

class Goblin : public EnemyImpl {
public:
    void rawAttack() {
        printf("Goblin Attack.\n");
    }
};

class BossEnemy : public Enemy {
public:
    BossEnemy(EnemyImpl* impl)
            : Enemy(impl) {}
    void SpecialAttack() {
        Attack();
        Attack();
    }
};


void main()
{
    Enemy* enemy = new Enemy( new Goblin() );
    enemy->Attack();

    BossEnemy* boss = new BossEnemy( new Goblin() );
    boss->SpecialAttack();
}

こうすることにより、今度はBossGoblinクラスを作成する必要は無くなった。
使うときにはどこが機能の階層で、実装の階層なのか、きちんと設計しておかないと。


参考文献:Java言語で学ぶデザインパターン入門
参考:
ギコ猫とBridgeパターン
Bridge パターン - Wikipedia