
Bridge を Java で
Bridge は、 構造に関するデザインパターンの一つで、 ビジネス・ロジックや巨大なクラスを独立して開発可能なクラス階層に分割します。
階層の一つ (抽象化と呼ばれる) は、 二つ目の階層 (実装) のオブジェクトへの参照を持ちます。 抽象化階層は、 その呼び出しのいくつか (場合によっては大多数) を実装階層のオブジェクトに委任します。 すべての実装は、 共通のインターフェースを持っているので、 抽象化の中で入れ替え可能です。
複雑度:
人気度:
使用例: Bridge パターンは、 クロス・プラットフォーム・アプリを扱う時、 複数の種類のデータベース・サーバーをサポートする時、 あるいはある種の API プロバイダー (クラウド・プラットフォーム、 ソーシャル・ネットワークなど) を複数利用したい場合に特に便利です。
見つけ方: Bridge は、 制御するものと、 それが依存するいくつかの異なるプラットフォームとが明らかに分かれていることから識別できます。
デバイスとリモート・コントロールの間のブリッジ
この例では、 複数のリモコンのクラスとそれらが制御するデバイスの間の分離を示します。
リモコン (コード例では、 Remote ) は、 抽象化階層で、 デバイスは実装階層です。 共通インターフェースのおかげで、 同じリモコンは異なるデバイスを操作でき、 逆も同様です。
Bridge パターンに従うと、 他方の層のコードを変更することなく、 クラスの変更や新規作成ができます。
devices
devices/Device.java: すべてのデバイスに共通なインターフェース
package refactoring_guru.bridge.example.devices; public interface Device { boolean isEnabled(); void enable(); void disable(); int getVolume(); void setVolume(int percent); int getChannel(); void setChannel(int channel); void printStatus(); }
devices/Radio.java: ラジオ
package refactoring_guru.bridge.example.devices; public class Radio implements Device { private boolean on = false; private int volume = 30; private int channel = 1; @Override public boolean isEnabled() { return on; } @Override public void enable() { on = true; } @Override public void disable() { on = false; } @Override public int getVolume() { return volume; } @Override public void setVolume(int volume) { if (volume > 100) { this.volume = 100; } else if (volume < 0) { this.volume = 0; } else { this.volume = volume; } } @Override public int getChannel() { return channel; } @Override public void setChannel(int channel) { this.channel = channel; } @Override public void printStatus() { System.out.println("------------------------------------"); System.out.println("| I'm radio."); System.out.println("| I'm " + (on ? "enabled" : "disabled")); System.out.println("| Current volume is " + volume + "%"); System.out.println("| Current channel is " + channel); System.out.println("------------------------------------\n"); } }
devices/Tv.java: テレビ
package refactoring_guru.bridge.example.devices; public class Tv implements Device { private boolean on = false; private int volume = 30; private int channel = 1; @Override public boolean isEnabled() { return on; } @Override public void enable() { on = true; } @Override public void disable() { on = false; } @Override public int getVolume() { return volume; } @Override public void setVolume(int volume) { if (volume > 100) { this.volume = 100; } else if (volume < 0) { this.volume = 0; } else { this.volume = volume; } } @Override public int getChannel() { return channel; } @Override public void setChannel(int channel) { this.channel = channel; } @Override public void printStatus() { System.out.println("------------------------------------"); System.out.println("| I'm TV set."); System.out.println("| I'm " + (on ? "enabled" : "disabled")); System.out.println("| Current volume is " + volume + "%"); System.out.println("| Current channel is " + channel); System.out.println("------------------------------------\n"); } }
remotes
remotes/Remote.java: すべてのリモコンに共通なインターフェース
package refactoring_guru.bridge.example.remotes; public interface Remote { void power(); void volumeDown(); void volumeUp(); void channelDown(); void channelUp(); }
remotes/BasicRemote.java: 基本的なリモコン
package refactoring_guru.bridge.example.remotes; import refactoring_guru.bridge.example.devices.Device; public class BasicRemote implements Remote { protected Device device; public BasicRemote() {} public BasicRemote(Device device) { this.device = device; } @Override public void power() { System.out.println("Remote: power toggle"); if (device.isEnabled()) { device.disable(); } else { device.enable(); } } @Override public void volumeDown() { System.out.println("Remote: volume down"); device.setVolume(device.getVolume() - 10); } @Override public void volumeUp() { System.out.println("Remote: volume up"); device.setVolume(device.getVolume() + 10); } @Override public void channelDown() { System.out.println("Remote: channel down"); device.setChannel(device.getChannel() - 1); } @Override public void channelUp() { System.out.println("Remote: channel up"); device.setChannel(device.getChannel() + 1); } }
remotes/AdvancedRemote.java: 高級リモコン
package refactoring_guru.bridge.example.remotes; import refactoring_guru.bridge.example.devices.Device; public class AdvancedRemote extends BasicRemote { public AdvancedRemote(Device device) { super.device = device; } public void mute() { System.out.println("Remote: mute"); device.setVolume(0); } }
Demo.java: クライアント・コード
package refactoring_guru.bridge.example; import refactoring_guru.bridge.example.devices.Device; import refactoring_guru.bridge.example.devices.Radio; import refactoring_guru.bridge.example.devices.Tv; import refactoring_guru.bridge.example.remotes.AdvancedRemote; import refactoring_guru.bridge.example.remotes.BasicRemote; public class Demo { public static void main(String[] args) { testDevice(new Tv()); testDevice(new Radio()); } public static void testDevice(Device device) { System.out.println("Tests with basic remote."); BasicRemote basicRemote = new BasicRemote(device); basicRemote.power(); device.printStatus(); System.out.println("Tests with advanced remote."); AdvancedRemote advancedRemote = new AdvancedRemote(device); advancedRemote.power(); advancedRemote.mute(); device.printStatus(); } }
OutputDemo.txt: 実行結果
Tests with basic remote. Remote: power toggle ------------------------------------ | I'm TV set. | I'm enabled | Current volume is 30% | Current channel is 1 ------------------------------------ Tests with advanced remote. Remote: power toggle Remote: mute ------------------------------------ | I'm TV set. | I'm disabled | Current volume is 0% | Current channel is 1 ------------------------------------ Tests with basic remote. Remote: power toggle ------------------------------------ | I'm radio. | I'm enabled | Current volume is 30% | Current channel is 1 ------------------------------------ Tests with advanced remote. Remote: power toggle Remote: mute ------------------------------------ | I'm radio. | I'm disabled | Current volume is 0% | Current channel is 1 ------------------------------------