Bridgeパターンの実装例 (Java)
[履歴] (2013/08/13 00:25:35)

概要

クラスの継承関係 (extends) を大別すると以下の二つがあります。

  • 親クラスがabstractで要請した機能を子クラスが実装する関係
  • 親クラスで不足していた機能 (メソッドなど) を子クラスで追加するための関係

それら継承関係を混在させて実装すると意図が不明瞭になってしまいます。そこでBridgeパターンでは二つの独立した継承関係を用意し、それぞれの継承関係のトップ同士を委譲によってつなぐ (Bridge) ことで継承を複数方向に行うことを可能にします。新たに継承が必要になったときには、どちらのツリーを拡張しようとしているのかを明確に意識できます。

サンプルコード

sample.java

class Sample {
    public static void main(String args[]) {
        int val=1;
        int arr[]={1};

        // プロジェクトのある時点まではAdderが有する機能で満足していた
        Adder intAdder = new Adder(new IntAdderImpl(val)); //abstractで要請された機能の実装方法を
        Adder arrAdder = new Adder(new ArrAdderImpl(arr)); //切り換えるためにはこの引数を差し替える
        intAdder.add(val);
        intAdder.print();
        arrAdder.add(arr);
        arrAdder.print();

        // 仕様変更か何かで機能が不足したため、Adderを継承したMultiAdderを用意し追加分を実装
        MultiAdder intMultiAdder = new MultiAdder(new IntAdderImpl(val));
        MultiAdder arrMultiAdder = new MultiAdder(new ArrAdderImpl(arr));
        intMultiAdder.add(val);
        intMultiAdder.multiAdd(val,8);
        intMultiAdder.print();
        arrMultiAdder.add(arr);
        arrMultiAdder.multiAdd(arr,8);
        arrMultiAdder.print();

        // 参考: Adder型変数にMultiAdderを代入することは可能
        Adder intAdder2 = new MultiAdder(new IntAdderImpl(val));
        intAdder2.add(val);
        // intAdder2.multiAdd(val,8); //ただしこれはエラー
        intAdder2.print();
    }
}

Adder.java

class Adder {
    private AdderImpl impl;
    public Adder(AdderImpl impl) {this.impl=impl;}
    public void add(Object val) {impl.rawAdd(val);}
    public void print() {impl.rawPrint();}
}

MultiAdder.java

class MultiAdder extends Adder {
    public MultiAdder(AdderImpl impl) {super(impl);}
    public void multiAdd(Object val, int times) {
        for(int i=0; i<times; ++i) add(val);
    }
}

AdderImpl.java

abstract class AdderImpl {
    public abstract void rawAdd(Object val);
    public abstract void rawPrint();
}

IntAdderImpl.java

class IntAdderImpl extends AdderImpl {
    private int val;
    public IntAdderImpl(int val) {this.val=val;}
    public void rawAdd(Object val) {this.val += (Integer)val;}
    public void rawPrint() {System.out.println(val);}
}

ArrAdderImpl.java

class ArrAdderImpl extends AdderImpl {
    private int arr[];
    public ArrAdderImpl(int arr[]) {
        this.arr = new int[arr.length];
        for(int i=0; i<arr.length; ++i) this.arr[i]=arr[i];
    }
    public void rawAdd(Object val) {
        int tmp[] = arr; //参照をコピー
        arr = new int[arr.length + ((int[])val).length]; //キャスト (型変換)
        for(int i=0; i<tmp.length; ++i) arr[i]=tmp[i];
        for(int i=0; i<((int[])val).length; ++i) arr[i+tmp.length]=((int[])val)[i];
    }
    public void rawPrint() {
        for(int i=0; i<arr.length; ++i) System.out.print(arr[i] + " ");
        System.out.println("");
    }
}

実行例

$ javac Adder.java  AdderImpl.java  ArrAdderImpl.java  IntAdderImpl.java  MultiAdder.java  sample.java && java Sample
2
1 1 
10
1 1 1 1 1 1 1 1 1 1 
2
関連ページ