はじめに
GoFデザインパターンの一つで,構造に関するパターンである
Bridgeパターンについて扱っていきたいと思います.
Bridgeパターンとは
Bridgeパターンは,機能と実装を分離して,それぞれを独立して変更できるようにしたものです.
Bridgeパターンは,クラスの階層が増大してしまう場合に有用とされています.
適用することで,クラスの階層が増えるのを抑え,柔軟なコードを作成することができます.
Bridgeパターンの実装(適用前)
リモコンとデバイスを例に考えます.
最初に,Bridgeパターンを適用しない場合で考えます.
この場合,テレビ用のリモコン,ラジオ用のリモコン,テレビ用の高度なリモコン,...など
様々なクラスが必要となり,クラス階層が複雑となります.
以下がソースコード例となります.
class TVStandardRemoteController {
private boolean isOn = false;
private int volume = 10;
public void togglePower() {
isOn = !isOn;
System.out.println("TV is " + (isOn ? "ON" : "OFF"));
}
public void volumeUp() {
volume++;
System.out.println("TV Volume: " + volume);
}
public void volumeDown() {
volume--;
System.out.println("TV Volume: " + volume);
}
}
class TVAdvancedRemoteController extends TVStandardRemote {
public void mute() {
System.out.println("Muting TV.");
}
}
class RadioStandardRemoteController {
private boolean isOn = false;
private int volume = 5;
public void togglePower() {
isOn = !isOn;
System.out.println("Radio is " + (isOn ? "ON" : "OFF"));
}
public void volumeUp() {
volume++;
System.out.println("Radio Volume: " + volume);
}
public void volumeDown() {
volume--;
System.out.println("Radio Volume: " + volume);
}
}
class RadioAdvancedRemoteController extends RadioStandardRemote {
public void mute() {
System.out.println("Muting Radio.");
}
}
public class NotBridgePattern {
public static void main(String[] args) {
TVStandardRemoteController tvRemote = new TVStandardRemote();
RadioAdvancedRemoteRemoteController radioRemote = new RadioAdvancedRemote();
System.out.println("Control TV:");
tvRemote.togglePower();
tvRemote.volumeUp();
System.out.println("\nControl Radio:");
radioRemote.togglePower();
radioRemote.mute();
}
}
ソースコードを見てもらうと,
クラスの数が多くなってしまっていることが確認できると思います.
さらに,Advancedリモコンの機能を持ったリモコンを新たに作りたいとなったときに,Advancedリモコンを継承して作ることが予想されます.
これにより,クラスの階層が増えてしまい拡張性などが損なわれてしまいます.
また,コードの重複があるなど様々な問題が見受けられます.
Bridgeパターンの実装(適用後)
では,Bridgeパターンを適用した場合を考えます.
以下がソースコード例となります.
interface Device {
void power();
void volumeUp();
void volumeDown();
}
class TV implements Device {
private boolean isOn = false;
private int volume = 10;
@Override
public void power() {
isOn = !isOn;
System.out.println("TV is " + (isOn ? "ON" : "OFF"));
}
@Override
public void volumeUp() {
volume++;
System.out.println("TV Volume: " + volume);
}
@Override
public void volumeDown() {
volume--;
System.out.println("TV Volume: " + volume);
}
}
class Radio implements Device {
private boolean isOn = false;
private int volume = 5;
@Override
public void power() {
isOn = !isOn;
System.out.println("Radio is " + (isOn ? "ON" : "OFF"));
}
@Override
public void volumeUp() {
volume++;
System.out.println("Radio Volume: " + volume);
}
@Override
public void volumeDown() {
volume--;
System.out.println("Radio Volume: " + volume);
}
}
class RemoteController {
protected Device device;
public RemoteController(Device device) {
this.device = device;
}
public void togglePower() {
device.power();
}
public void volumeUp() {
device.volumeUp();
}
public void volumeDown() {
device.volumeDown();
}
}
class AdvancedRemoteController extends RemoteController {
public AdvancedRemoteController(Device device) {
super(device);
}
public void mute() {
System.out.println("Muting the device.");
}
}
public class BridgePattern {
public static void main(String[] args) {
Device tv = new TV();
Device radio = new Radio();
RemoteController tvRemoteController = new RemoteController(tv);
AdvancedRemoteController radioRemote = new AdvancedRemoteController(radio);
System.out.println("Control TV:");
tvRemote.togglePower();
tvRemote.volumeUp();
System.out.println("\nControl Radio:");
radioRemote.togglePower();
radioRemote.mute();
}
}
上記コードのように,機能であるDevice, 実装であるRemoteControllerに分けました.
これにより,デバイスを追加したいのであればDeviceをimplementsしたクラスを作れば良いし,
新しいリモコンを追加したいのであればRemoteControllerを継承したクラスを作ることで,
組み合わせごとのクラスを作る必要性がなくなります.
例えば,新しいデバイスであるSpeakerを追加したいのであれば,Deviceインタフェースを実装するだけでよいです.
さらに,新しいリモコンであるVoiceRemoteControllerを追加したいのであれば,Remoteを拡張するだけで済みます.
このように,Bridgeパターンを適用して機能と実装を分離することで
拡張性や柔軟性が高くなります.
とはいえ,Bridgeパターンは新しい接続方法を後で追加しなければならないという状況に陥らない限り,使うことは推奨されません.
これは,Bridgeパターンを使うとコードが複雑になってしまうためです.
最後に
今回は短くなってしまいましたが,Bridgeパターンについてまとめました.
デザインパターンを書き続けたことにより,インタフェースや抽象化の理解が深まったように感じますし,それらの利点もすんなりと理解できるようになりました.
勉強意欲とやる気があるうちに,GoFデザインパターン全種類を
目に通す + それらの役割を抑えておきたいですね...