Bridge

「機能のクラス階層」と「実装のクラス階層」を分けて,これらのクラスを橋渡しする構造をとる.クラス構造を分離することで,継承が深くなったり,機能が重複したりすることを防ぐことができる.

「機能のクラス階層」では,上位のクラスに無い機能を追加するために,上位のクラスを継承して新しいクラスを構成する.スーパークラスは,基本的な機能を持っている.サブクラスでは,スーパークラスにない機能が追加される.すなわち,機能の追加を構成する階層である.

「実装のクラス階層」では,決められたAPIを使った処理内容について,多種にわたる実装があるときに,構成する階層である.スーパークラスでは,抽象メソッドとしてインターフェイスを定義している.サブクラスでは,具象メソッドとしてインターフェイスを実装する.

これらを分離しない場合には,機能のクラス階層に加えて,実装のクラス階層が継承される形になることが考えられる.

クラス構造

たとえば,自動車の種類をもとに階層化したとするとき,その動力に関する種類が多数あるとき,具体化された自動車それぞれに,動力の種類が割り当てられることになる.


見方を変えて,車種に分けてみると,今後は,車種ごとに動力の異なるクラスを設ける必要が出てくる.

このように,複数の要素を持つとき,階層が深くなる,機能が重複する,などの弊害が生じる.

そこで,「機能のクラス構造」として車の種類を構成し,「実装のクラス構造」として,動力となるエネルギーの種類を構成する.そのうえで,車がエネルギーとなる動力を持つというクラス内部構造にする.これにより,動力を適切に選ぶことができる.


ソースコード

車の種類について,車のタイプについて機能ごとに分け,実装の違いとして,動力の違いで分ける.各車のタイプそれぞれで,動力の違いをフィールドとして持つことで,関係づける.

実装のクラス

他方,「実装のクラス階層」として,動力を設定するための抽象クラスとして動作を定義する「Movement」クラスを定義する.

abstract public class Movement
{
    abstract public void forward();
    abstract public void back();
    abstract public void turn(Boolean side);
}

Movementクラスを継承して,実装するための具体的なクラスとして,「ガソリンクラス」(GasolineMovement)や「電気クラス」(MotorMovement)を定義する.それぞれ,動作のメソッドを実装しているが,内容はクラスごとに異なる.

public class GasolineMovement extends Movement
{
    @Override
    public void forward()
    {
        System.out.println("ブォーン「前進します」");
    }
    @Override
    public void back()
    {
        System.out.println("ブッフ「バックします」");
    }
    @Override
    public void turn(Boolean side)
    {
        if(side)
        {
            System.out.println("シュッ「右に曲がります」");
        }
        else
        {
            System.out.println("シュッ「左に曲がります」");
        }
    }
}
public class MotorMovement extends Movement
{
    @Override
    public void forward()
    {
        System.out.println("シュウィーン「前進します」");
    }
    @Override
    public void back()
    {
        System.out.println("シュッ「バックします」");
    }
    @Override
    public void turn(Boolean side)
    {
        if(side)
        {
            System.out.println("シュッ「右に曲がります」");
        }
        else
        {
            System.out.println("シュッ「左に曲がります」");
        }
    }
}

機能のクラス

抽象クラスとして定義する車クラス(Car)のコードに示す.車クラスは,車として継承するクラスで具体化する「排気量」メソッドを持つ.さらに,実装によって異なる,動力源を表すMovementクラスの動力フィールド(move)を持つ.これによって,「実装のクラス構造」を集約の関係でつながっている.

さらに,動作については,委譲を利用しており,前進やバックなどのメソッドでは,動力源を使っている.基本的な機能を車クラスは持っているが,それらの機能に加えて,追加することができる.

public class Car
{
    private Movement move;
    Car(Movement move)
    {
        this.move = move;
    }
    public void forward()
    {
        move.forward();
    }
    public void back()
    {
        move.back();
    }
    public void turn(Boolean side)
    {
        move.turn(side);
    }
}

車クラスを継承して,トラッククラス(Truck),バスクラス(Bus)を定義する.

トラッククラスやバスクラスでは,バックするときに,単な動作を遂行する他に,大型である故に周辺への注意喚起として,音声にて伝えることが見られないだろうか.これを追加する機能として実装するために,継承したクラスで,メソッドをオーバーライドしている.

public class Truck extends Car
{
    Truck(Movement move)
    {
        super(move);
    }
    public void back()
    {
        System.out.print("ピーッピーッ");
        super.back();
    }
}
public class Bus extends Car
{
    Bus(Movement move)
    {
        super(move);
    }
    public void back()
    {
        System.out.print("「後ろに下がります.ご注意ください.」");
        super.back();
    }
}

確認

動作を確認するために,実際にインスタンス化して,実行してみる. 自動車の動作については,動力源であるMovementに委譲しているので,各種車を作る時には,動力源を設定するようにしている.そこで,動力源をインスタンス化して,トラック,バスをインスタンス化している. 車の動作について,電気モータであっても,ガソリンエンジンであっても,同じメソッドで操作することができている様子が確認できる.動力源の違いや,機能の違いによって,表示が変化していることが分かるだろうか.
public class Main
{
    public static void main(String[] args)
    {
        System.out.println("-----電気トラックを作ります-----");
        Car hino2t = new Truck(new MotorMovement());
        hino2t.back();
        hino2t.forward();
        hino2t.turn(false);
        System.out.println("-----ガソリンバスを作ります-----");
        Car isuzuBus = new Bus(new GasolineMovement());
        isuzuBus.forward();
        isuzuBus.turn(true);
        isuzuBus.back();
    }
}
-----電気トラックを作ります-----
ピーッピーッシュッ「バックします」
シュウィーン「前進します」
シュッ「左に曲がります」
-----ガソリンバスを作ります-----
ブォーン「前進します」
シュッ「右に曲がります」
「後ろに下がります.ご注意ください.」ブッフ「バックします」

CarクラスとMovementクラスが橋の両端といえるだろうか.さらに,その橋になるのが,Carクラスのフィールドとして定義されたMovementフィールドである.このフィールドが指すオブジェクトを介して,実際の動作を得ている.この仕組みは「委譲」である.

演習

(1)追加

車の種類として,軽自動車を追加する.さらに,水素による動力を追加する.そのうえで,水素軽自動車をインスタンス化する.

(2)ブリッジクラスの作成

消防車や救急車を想定した,サイレンをもつ車クラスを機能のクラスとし,サイレンを実装のクラスとする.サイレン車は,サイレンの実装をサイレンクラスに委譲するものとする.

応用課題

ハイブリッドな動力源を設定したい時には,どのように実装したらよいだろうか?

提出

締切日時:2021年1月6日17:00
提出先:Blackboard
科目/題名:オブジェクト指向/Bridge課題
内容:Bridgeパターンの演習「(2)ブリッジクラスの作成」の課題
   http://teacher.nagano-nct.ac.jp/fujita/Bridge.php
提出物:(1)作成したクラスのソースコード,(2)実行した結果,(3)クラス図
(1)-(3)を含むPDF.
     (例:875_藤田悠.pdf)