【デザインパターン】JavaのBridgeパターンについて
今回はbridgeパターンについて解説します。
Bridge
Bridgeパターンは機能の拡張と実装の拡張を分けるために存在します。
分けるためには機能の階層と実装の階層を作り、それらをBridge(橋)で渡す必要があります。
機能のクラス階層
あるクラスSomethingがあるとします。 このクラスに新しい機能を追加しようとしたらSomethingのサブクラスを作ります。
public class Something {
	public void doSomething() { ... }
}
public class SomethingGood extends Something {
	public void doSomethingAnother() { ... }
}
現在の階層は以下です。
Something
└ SomethingGood
さらに機能を追加したいと思いました。
public class SomethingBetter extends Something {
	public void doSomethingElse() { ... }
}
現在の階層は以下です。
Something
└ SomethingGood
	└ SomethingBetter
このように機能を継ぎ足していく場所を**機能のクラス階層**と言います。
実装のクラス階層
Templateメソッドを思い出してください。 スーパークラスは一連のメソッド群を抽象メソッドとして宣言しサブクラスでそれらを実装します。 ここにもクラス階層が存在します。 スーパークラスとしてAbstractClassを作り、その具体的なメソッドはConcreteClassで実装します。
public abstract class AbstractClass {
	public abstract void doSomething();
}
public class ConcreteClass {
	public void doSomething() {
		System.out.println("do something");
	}
}
この時の階層は以下のようになります。
AbstractClass
└ ConcreteClass
これを**実装のクラス階層**と呼びます。
まとめると
- 機能のクラス階層では新しい機能の追加
 - 実装のクラス階層では新しい処理の中身の追加
 
を行っています。
絵を描く時にクレヨンで描く、筆で描く(実装のクラス階層)の追加と似顔絵を描く、風景画を描く(機能のクラス階層)の追加を別々に行うイメージです。
今回はデバイスごとの通知機能を実装します。
実装(機能のクラス階層)
Notification
**機能のクラス階層**の最上位に当たるクラスです。
implフィールドはNotificationクラスの実装を表すインスタンスです。
ここではimplで実装されている処理を用いて1人に対して知らせる機能、Notifyメソッドを実装しています。
public class Notification {
    private NotificationImpl impl;
    public Notification(NotificationImpl impl) {
        this.impl = impl;
    }
    protected void setMessage(String title, String message) {
        impl.setMessage(title, message);
    }
    protected void sendMessage(String to) {
        impl.sendMessage(to);
    }
    public final void Notify(String title, String message, String to) {
        setMessage(title, message);
        sendMessage(to);
    }
}
MultiNotification
ここでは機能の追加を行いたいので、複数人に対して通知をするmultiNotifyを実装します。
public class MultiNotification extends Notification {
    public MultiNotification(NotificationImpl impl) {
        super(impl);
    }
    public void MultiNotify(String title, String message, String[] to) {
        setMessage(title, message);
        for (String s : to) {
            sendMessage(s);
        }
    }
}
実装(実装のクラス階層)
NotificatioinImpl
**実装のクラス階層**の最上位に位置します。
このクラスが具体的な実装をスーパークラスのNotificationへ橋渡ししています。
setMessageはテンプレートメソッドとして具体的な処理を記述しています。(今回はメッセージの形式がデバイスごとで変わらないため)
public abstract class NotificationImpl {
    protected StringBuilder message = new StringBuilder();
    protected void setMessage(String title, String message){
        this.message.append("Title: 【" + title + "】\n");
        this.message.append("----------------------------\n ");
        this.message.append(message + "\n");
        this.message.append("----------------------------\n ");
    }
    protected abstract void sendMessage(String to);
}
ここが肝で、NotificationImplが存在しないと実装と機能を分離することができなくなってしまいます。 時間がある方は試しに直接スーパークラスをオーバーライドする形でメソッドを記述してみてください。 機能がNotificationクラスに集中してしまいます。 NotificationImplを噛ませて具体的な実装を機能から隠すことでそれぞれ別々にスケールさせることができるようになり、いわゆる神クラスを防ぐことにもつながります。
iPhoneNotificationImpl
iPhoneに対してメッセージを送る具体的なメソッドを記述します。
public class iPhoneNotificationImpl extends NotificationImpl {
    @Override
    protected void sendMessage(String to) {
        System.out.println("Send message to \"" + to + "@icloud.com\"");
        System.out.println(this.message);
    }
}
AndroidNotificationImpl
Androidに対してメッセージを送る具体的なメソッドを記述します。
public class AndroidNotificationImpl extends NotificationImpl {
    @Override
    protected void sendMessage(String to) {
        System.out.println("Send message to \"" + to + "@gmail.com\"");
        System.out.println(this.message);
    }
}
実行
Mainクラス
機能と実装を分けたことにより、それぞれを組み合わせて柔軟な処理を少ない依存関係で実現できました。
public class Main {
    public static void main(String[] args) {
        Notification notification = new Notification(new iPhoneNotificationImpl());
        notification.Notify("Hello", "This is a message for iPhone", "John");
        notification = new Notification(new AndroidNotificationImpl());
        notification.Notify("Hello", "This is a message for Android", "Mary");
        MultiNotification multiNotification = new MultiNotification(new iPhoneNotificationImpl());
        multiNotification.MultiNotify("Hello", "This is a message for iPhone", new String[] {"Kurimatsu", "Steve"});
        multiNotification = new MultiNotification(new AndroidNotificationImpl());
        multiNotification.MultiNotify("Hello", "This is a message for Android", new String[] {"Tom", "Jerry"});
    }
}
実行結果
Send message to "John@icloud.com"
Title: 【Hello】
----------------------------
 This is a message for iPhone
----------------------------
 
Send message to "Mary@gmail.com"
Title: 【Hello】
----------------------------
 This is a message for Android
----------------------------
 
Send message to "Kurimatsu@icloud.com"
Title: 【Hello】
----------------------------
 This is a message for iPhone
----------------------------
 
Send message to "Steve@icloud.com"
Title: 【Hello】
----------------------------
 This is a message for iPhone
----------------------------
 
Send message to "Tom@gmail.com"
Title: 【Hello】
----------------------------
 This is a message for Android
----------------------------
 
Send message to "Jerry@gmail.com"
Title: 【Hello】
----------------------------
 This is a message for Android
----------------------------
See you later 👋
0