ある数列[1,3,2,5,4]をソートしたいとします.このときソートの方法は何通りかあります.このとき「ソートをする」ということが機能にあたります.そして「ソートをする方法」,たとえばクイックソートやマージソートなどが実装にあたります.クラスを拡張しようとするときには,具体的には「機能の拡張」と「実装の拡張」のいずれかであることが多いです.
TECSCOREにわかりやすい例が載っていたので,それに添って Bridge パターンが必要になるシチュエーションを見てみます.ソート機能を持つ抽象クラスSoter
に対して,実装クラスQuickSorter
とBubbleSorter
を以下のように作成したとします.機能はSoter
,実装はQuickSorter
, BubbleSorter
に分離されていることに注意します.
public abstract class Sorter { public abstract void sort(Object obj[]); } public class QuickSorter extends Sorter { public void sort(Object obj[]){ // クイックソートで obj[] をソートする } } public class BubbleSorter extends Sorter { public void sort(Object obj[]){ // バブルソートで obj[] をソートする } }
現状のコードに新しい「実装」を増やすことは容易いです.単にSoter
を extends した実装クラスを書けば良いだけだからです.しかしながら新しい「機能」を増やすためにはどうしたら良いでしょうか.ためしにソートに加え,それにかかった時間を表示する機能を追加したい場合を考えてみます.するとコードは以下のようになります.
public abstract class TimerSorter extends Sorter { public void timerSorter(Object obj[]){ long start = System.currentTimeMillis(); sort(obj); long end = System.currentTimeMillis(); System.out.println("time:"+(end - start)); } }
ソートの実装自体は先ほど実装してあるQucickSort
, BubbleSort
クラスを使いたいとします.しかしながらよくよく考えてみるとTimeSorter
にそれらの実装を与えることができません(Java1.7 以下での話?).そこで残念ながらそれらのTimeSorter
に対応した実装クラスQuickTimeSorter
とBubbleTimeSorter
を書かなければなりません.図で表すと以下のような具合になります.
このような状況に上手く対処するために Bridge Pattern が用いられます.
Bridge Pattern では実装の部分を別の階層に切り離すことによって,これまでに見てきた例のような問題を解決します.まずはコード例を見てみます.
public class Sorter { private SortImpl sortImpl; public Sorter(SortImpl sortImpl) { this.sortImpl = sortImpl; } public void sort(Object obj[]) { sortImpl.sort(obj); } } public abstract class SortImpl { public abstract void sort(Object obj[]); } public class QuickSortImpl extends SortImpl { public void sort(Object obj[]) { // クイックソートで obj[] をソートする } } public class BubbleSortImpl extends SortImpl { public void sort(Object obj[]) { // バブルソートで obj[] をソートする } }
このように機能を実現するための方法がSortImpl
に委譲
されているため,実装の追加はもちろん,機能の追加は容易になります.まとめとして Bridge Pattern のクラス図を示しておきます.
時間経過に伴って機能が拡張されていくバージョンアップの過程と,実装タイプのバリエーションを豊富に広げる品揃えの充実具合を,別個に分けて,整理して管理できる(GoF の 23 のデザインパターンを,Java で活用するための一覧表より)
この部分については個人的に思いついた内容なので,実際に良い方法なのか微妙なところですが,とりあえず書いておきます.Java8 ではインターフェースがデフォルト実装を持てるようになったため,先の例ででてきたSorter
を抽象クラスではなくインターフェースとして定義しておけば,「実装の拡張」はもちろん,「機能の拡張」もすっきりおこなえるかなと思いました.ソートにかかる時間を表示する timeSort 機能を追加したあとのクラス的にはこんな具合になると思います.
現時点でこの方法が抱えている問題は,Sorter
が抽象クラスではなくインターフェースになったので状態(プロパティ)を持てなくなってしまったという点だと考えていますが,どうでしょうか.
ウェブ界隈でエンジニアとして労働活動に励んでいる @gomi_ningen 個人のブログです