Bridgeパターン

      2018/05/23

個人的にはC++時代に有効だった手法かなと。
C#やJavaで使うとなるとインターフェースが存在するため、直接インターフェースを実装せずに1つオブジェクトを挟む感じでしょうか。

通常の継承

abstract class SpellBase
{
    abstract public int Low();
    abstract public int Middle();
    abstract public int High();
}

上記を継承したクラスを作成する場合、

class Mera : SpellBase
{
    public override int Low() {
        int value;
        // 色々な計算
        return value;
    }

    public override int Middle() {
        int value;
        // 色々な計算
        return value;
    }

    public override int High() {
        int value;
        // 色々な計算
        return value;
    }
}
class Gira : SpellBase
{
    public override int Low() {
        int value;
        // 色々な計算
        return value;
    }

    public override int Middle() {
        int value;
        // 色々な計算
        return value;
    }

    public override int High() {
        int value;
        // 色々な計算
        return value;
    }
}

上記でも問題ないが、例えば「ハイテンション状態なら、各戻り値を2倍する」。とかの仕様が追加になると、使用側もしくは各メソッドで判定が必要になる。
そこでBridgeパターンが登場です。

Bridgeパターン

interface ISpellImplementer
{
    int Low();
    int Middle();
    int High();
}

橋渡しクラス

class SpellBridge
{
    protected ISpellImplementer _spellImple;

    public SpellBridge(ISpellImplementer spellImple) {
        this._spellImple = spellImple;
    }

    public virtual int Low() {
        return this._spellImple.Low();
    }

    public virtual int Middle() {
        return this._spellImple.Middle();
    }

    public virtual int High() {
        return this._spellImple.High();
    }
}

計算式実装クラス

class Mera : ISpellImplementer
{
    public int Low() {
        int value;
        // 色々な計算
        return value;
    }

    public int Middle() {
        int value;
        // 色々な計算
        return value;
    }

    public int High() {
        int value;
        // 色々な計算
        return value;
    }
}
class Gira : ISpellImplementer
{
    public int Low() {
        int value;
        // 色々な計算
        return value;
    }

    public int Middle() {
        int value;
        // 色々な計算
        return value;
    }

    public int High() {
        int value;
        // 色々な計算
        return value;
    }
}

ハイテンション状態で使用するクラス

class SpellBridgeWithHighTension : SpellBridge
{
    public SpellBridgeWithHighTension(ISpellImplementer spellImple)
        :base(spellImple) {
    }

    public override int Low() {
        int value = this._spellImple.Low();
        return value * 2;
    }

    public override int Middle() {
        int value = this._spellImple.Middle();
        return value * 2;
    }

    public override int High() {
        int value = this._spellImple.High();
        return value * 2;
    }
}

ハイテンション状態かどうかを橋渡しクラスで判断させる場合

class SpellBridge
{
    protected ISpellImplementer _spellImple;

    public SpellBridge(ISpellImplementer spellImple) {
        this._spellImple = spellImple;
    }

    public virtual int Low() {
        int value = this._spellImple.Low();
        if (IsHighTension()) {
            return value * 2;
        }
        return value;
    }

    public virtual int Middle() {
        int value = this._spellImple.Middle();
        if (IsHighTension()) {
            return value * 2;
        }
        return value;
    }

    public virtual int High() {
        int value = this._spellImple.High();
        if (IsHighTension()) {
            return value * 2;
        }
        return value;
    }
}

IsHighTension()はハイテンションかどうかを取得するメソッドを想定。

このサンプルではあまりメリットはありませんが、Bridgeパターンを使用することで、変更箇所が絞れました。
後々、実行時間を測定するケースが出てきそうな処理などはこういう風に実装しておくと、試験用・実運用などで切り分けが簡単になりそうです。

 - 設計 ,