目次へ

9. Bridge パターン

  • 2012/04/26 一部修正しました

9.1 Bridge パターンとは

第9章では Bridge パターンを学びます。Bridgeパターンとは、「Bridge」すなわち「橋」の役割を果たすパターンです。Bridgeパターンを利用することで、機能と実装を分離して、それぞれを独立に拡張することができるようになります。

例えば、ある methodA というメソッドを持つクラス MyClassA は、methodA メソッドの実装が異なる MyClassASub1、MyClassASub2 という2つのクラスによって継承されているとします。このとき、MyClassA にmethodB というメソッドを追加するために、MyClassB クラスという MyClassA を継承するクラスを作成したことを考えてください。

「MyClassB クラスを作成した」図

このとき、MyClassB でも、MyClassASub1、MyClassASub2 で実装している methodA と同じ実装を利用したい場合、MyClassB クラスを継承する MyClassBSub1、MyClassBSub2 といったクラスを作成する必要があります。

「MyClassBSub1、MyClassBSub2 」を作成した図

今回のように、2つのクラスだけなら手間はそんなにかかりませんが、場合によっては、MyClassA に機能を追加するためのサブクラス MyClassX を作成するたびに、何十という MyClassXSub・・・ というサブクラスを作成することが必要となります。Bridge パターンは、機能を拡張するための階層と実装を拡張するための階層を分離することにより、このようなわずらわしさを解消し、拡張を容易にするものです。

9.2 サンプルケース

サンプルケースでは、ソート機能を持つ抽象クラスSorter と、この Sorter クラスで定義されている抽象メソッドである sort(Object obj[]) メソッドを実装するクラス (QuickSorter クラス、BubbleSorter クラス) について考えて見ましょう。Sorter クラス、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[] をソートする 
        ・・・・ 
    } 
}

この状態のクラス図を見てみましょう。

クラス図1

さて、ここであなたは、Sorterクラスにソートにかかった時間を表示する機能を持つ timerSorter メソッドを追加したくなりました。そこで、Sorter クラスを拡張する TimerSorter クラスを作成しました。ソースコードは以下のようになります。

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)); 
    } 
}

このような場合、以上のような設計ではとても面倒なことに気づかれたでしょうか?そうです、TimerSorter クラスに sort の実装を与えられないのです。せっかく QuickSorter クラスや BubbleSorter クラスが存在しているにもかかわらず、同様の実装を与えるために、TimerSorter クラスを拡張する TimerQuickSorter クラスや TimerBubbleSorter クラスを作成する必要があるのです。このときのクラス図は以下のようになります。

クラス図2

このような問題を回避するためのパターンとして Bridge パターンが利用されます。Bridge パターンでは、実装の変更が考えられるメソッドに関しては、実装用のクラス階層に委譲するように設計します。実装用のクラス階層とは、ここでは sort メソッドの実装を与えるクラス階層として、SortImple クラスを親とするクラス階層を考えます。具体的には、Sorter クラス、SortImple クラスを以下のようなコーディングにしておきます。

public class Sorter{ 
    private SortImple sortImple; 
    public Sorter(SortImple sortImple){ 
        this.sortImple = sortImple; 
    } 
    public void sort(Object obj[]){ 
        sortImple.sort(obj); 
    } 
}
public abstract class SortImple{ 
    public abstract void sort(Object obj[]); 
}

そして、実際にソートの機能を実装する QuickSorter クラスと BubbleSorter クラスを SortImple クラスを継承するクラスとして作成するようにします。

public class QuickSortImple extends SortImple { 
    public void sort(Object obj[]){ 
        // クイックソートで obj[] をソートする 
        ・・・・ 
    } 
}
public class BubbleSortImple extends SortImple{ 
    public void sort(Object obj[]){ 
        // バブルソートで obj[] をソートする 
        ・・・・ 
    } 
}

この状態でのクラス図を確認しておきましょう。

クラス図3

このような設計にしておくと、機能を追加するために、Sorter クラスを拡張して作成した新しいクラスでも、すでに存在する実装部分を利用することができるようになります。例えば、Sorter クラスを拡張する TimerSorter クラスを作成する場合には、以下のようなコーディングとなります。

public class TimerSorter extends Sorter{ 
    public TimerSorter(SortImple sortImple){ 
        super(sortImple); 
    } 
    public void timerSort(Object obj[]){ 
        long start = System.currentTimeMillis(); 
        sort(obj); 
        long end = System.currentTimeMillis(); 
        System.out.println("time:"+(end - start)); 
    } 
}

このように、機能を拡張するためのクラス階層と、実装を拡張するためのクラス階層を分けておくことで、実装階層クラスと機能拡張クラスを好みの組み合わせで利用することができるようになります。今回の例では、Sorter クラスと SortImple クラスが機能拡張クラス階層と実装拡張クラス階層を橋渡しする役目を果たしています。

Sorter クラスと SortImple クラスが機能拡張クラス階層と実装拡張クラス階層を橋渡しした図

9.3 Bridge パターンのまとめ

Bridge パターンの一般的なクラス図は以下のようになります。

Bridge パターンの一般的なクラス図
[引用] 『Java言語で学ぶ デザインパターン入門』(結城浩 ソフトバンクパブリッシング株式会社出版 2001年)

↑このページの先頭へ

こちらもチェック!

PR
  • XMLDB.jp