クラスの継承関係 (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
0
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。
関連記事
- Spring Security フォームログインのサンプルコードSpring フレームワークによる Web アプリケーション開発で、ログイン処理を実装する際は Spring Security が便利です。ここでは特に Spring Boot で Web アプリケーションを開発する場合を対象とし、フォームによる ID/Password ログインを行うためのサンプルコードをまとめます。 公式ドキュメント [Spring Security チュートリアル](http...
- Java配列の宣言方法 (C/C++との違い)Javaの配列 Javaの配列宣言方法はC/C++と似ているようで若干異なる。 初期化しない場合 C/C++の int array[10]; はJavaでは int array[] = new int[10]; となる。同様にC/C++の int array[3][3]; はJavaでは int array[][] = new int[3][3]; となる。 初期化
- PlantUML による UML 図の描き方PlantUML はテキスト形式で表現されたシーケンス図やクラス図といった UML (Unified Modeling Language) 図の情報から画像を生成するためのツールです。簡単な使い方をまとめます。 インストール方法の選択 Atom や Eclipse のプラグインをインストールしてエディタから利用する方法、JAR をダウンロードして Java コマンドで実行する方法、Redmine ...
- Akka HTTP サンプルコード (Scala)Akka アクターを用いて実装された汎用 HTTP フレームワークです。Spray の後継です。コアモジュールである akka-http-core は 2016/2/17 に experimental が外れました。akka-http などのいくつかのサブモジュールは 2016/3/1 現在 experimental のままですが、基本的な
- Kestrel の使用例Kestrel は Message Queue (MQ) の実装のひとつです。一般に MQ はアプリケーション間やプロセス間、スレッド間で非同期に通信するために用いられます。メッセージの送信側は MQ に書き込めば受信側の応答を待たずに次の処理に非同期に進むことができます。Kestrel はわずか 2500 行程の Scala で実装されており JVM で動作します。MQ 自体はメモリ上に存在する...