Bridgeパターン_katagiri.md
参照: https://refactoring.guru/ja/design-patterns/bridge
構造的パターンの一つ 巨大クラス、密接に関連するクラスの集まりを抽象と実装に分解し、それらを独立に変更できるようにする。
幾何学クラスで形と色を持つクラスを作成する。
形: 正方形、円、三角形(それぞれに属性と描画メソッドを持つ) 色: 赤、緑、青(それぞれに属性を持つ)
クラスで形と色を持つクラスを作成すると 親クラスを作ったとして、そのサブクラスが9つ必要になる。 赤正方形、緑正方形、青正方形、赤円、緑円、青円、赤三角形、緑三角形、青三角形
Pythonで素直に実装すると以下のようになる。
class Shape:
def __init__(self, color):
self.color = color
def draw(self):
pass
class RedSquare(Shape):
def __init__(self):
super().__init__("Red")
def draw(self):
print("Draw Red Square")
形クラスと色クラスを分ける
class Color:
def __init__(self, color):
self.color = color
class Shape:
def __init__(self, color):
self.color = color
def draw(self):
pass
class Square(Shape):
def draw(self):
print(f"Draw {self.color.color} Square")
class Circle(Shape):
def draw(self):
print(f"Draw {self.color.color} Circle")
class Red(Color):
def __init__(self):
super().__init__("Red")
class Green(Color):
def __init__(self):
super().__init__("Green")
# use
red = Red()
green = Green()
square = Square(red)
circle = Circle(green)
square.draw()
circle.draw()
Shapeが抽象クラス, Colorが実装クラス Square, Circleが"具体的な抽象クラス" Red, Greenが"具体的な実装クラス"となる。
APIとGUI
抽象クラスとしてGUIと実装クラスとしてAPIを作成し、それぞれの具体クラスを作成する。
GUIは顧客向け、開発者向け、コンソール向けなど各実装がある。 APIはバージョン1, バージョン2など各実装がある(?)
各種デバイスがあり、それをデバイスを操作するリモコンがある場合。 ラジオ、テレビ、etc. 単純リモコン、多機能リモコン、etc.
Bridpeパターンを用いることで、デバイスとリモコンを独立に変更できるようになる。 抽象クラスとしてRemotoクラスと実装クラスとしてDeviceクラスに分ける。
Deviceクラスには抽象化された操作群があり、それを継承した具体的実装クラスがある。 RemotoクラスはDeviceクラスを持ち、それを継承した具体的抽象クラスがある。
class Device:
def __init__(self):
pass
def is_enable(self):
pass
def enable(self):
pass
def disable(self):
pass
def get_volume(self):
pass
def set_volume(self, volume):
pass
def get_channel(self):
pass
def set_channel(self, channel):
pass
class Remote:
def __init__(self, device):
self.device = device
def toggle_power(self):
if self.device.is_enable():
self.device.disable()
else:
self.device.enable()
def volume_down(self):
self.device.set_volume(self.device.get_volume() - 10)
def volume_up(self):
self.device.set_volume(self.device.get_volume() + 10)
def channel_down(self):
self.device.set_channel(self.device.get_channel() - 1)
def channel_up(self):
self.device.set_channel(self.device.get_channel() + 1)
class TV(Device):
def __init__(self):
self.enable = False
self.volume = 50
self.channel = 1
def is_enable(self):
return self.enable
def enable(self):
self.enable = True
def disable(self):
self.enable = False
def get_volume(self):
return self.volume
def set_volume(self, volume):
self.volume = volume
def get_channel(self):
return self.channel
def set_channel(self, channel):
self.channel = channel
class SimpleRemote(Remote):
pass
class AdvancedRemote(Remote):
def mute(self):
self.device.set_volume(0)
# use
tv = TV()
simple_remote = SimpleRemote(tv)
simple_remote.toggle_power()
advanced_remote = AdvancedRemote(tv)
advanced_remote.toggle_power()
# 適応性
1. ちょっとした違いの変種がある場合、変種が増えれば増える程全体把握が難しくなる。
そのような場合にBridgeパターンを用いることで、変種を独立に変更できるようになる。
2. クラスが幾つかの直交する次元での拡張が必要な場合、Bridgeパターンを用いることで、それぞれの次元を独立に拡張できるようになる。
3. 実行時に実装を選択する必要がある場合、Bridgeパターンを用いることで、実装を独立に選択できるようになる。
例: 出力先がファイルか、プリンタか、画面か、etc.
( Strategy パターンとは違うらしい。。)
# 実装の手順
1. 直交する次元の特定
2. 抽象クラスの特定
3. クラスの操作を抽象化(絶対に使うもののみ)
4. 実装クラスの作成
5. 抽象クラス内では実装クラスを参照し操作は実装クラスに委譲する
## 長所と短所
長所
- クラスの変更が容易
短所
- 高度に密着しているクラスには適用しにくい(次元が分割できない)
## 他パターンとの関係
1. Adapterパターンとの違い Builderは設計当初から使われるが、Adapterは既存のクラスに適用する
2. State, Startegyパターンとの違い State, Startegyは状態やアルゴリズムを独立に変更するが、Bridgeは実装を独立に変更する
3. Abstruct Factoryパターンとの関係 一緒に使うことが多い。
4. Builderパターンとの関係 一緒に使うことが多い。