Bridge Design Patternは構造的デザインパターンであり、抽象化と実装を分離するものです。この記事では、PythonにおけるBridge Design Patternの動機と実装について説明します。
デザインパターンとは、ソフトウェアエンジニアリングにおける一般的なアーキテクチャ上の問題に対する標準化されたプラクティスや解決策の集合を指します。
ブリッジのデザインパターンに隠された動機
Bridgeパターンは、いわゆるデカルト積の複雑性の爆発を防ぐものである。
この問題は、例によって明らかになる。例えば、あなたが飛行機を実装しているとしよう。それは軍用機であったり、商用機であったりする。さらに、旅客機・兵員機・貨物機であることもできます。
これを実装する一つの方法は、MilitaryPassenger
、MilitaryCargo
、CommercialPassenger
、CommercialCargo
という飛行機を持たせることです。
ここで、デカルト積の複雑さは 2 x 2 = 4
となります。この規模ではこの数字は画期的なものではありませんが、クラスやバリエーションを増やすと指数関数的に増加し、あっという間に手に負えなくなってしまうでしょう。
ブリッジパターンは、クラス(Airplane
の実装)とその特性(旅客機なのか貨物機なのか)の間の橋渡しとして使われます。このパターンでは、継承よりもコンポジションを優先します。
このパターンを使うと、異なるカテゴリの型ごとに1つのクラスを作成することになります。例えば、この例では、 CommercialPlane
と MilitaryPlane
をエンティティとして、 CargoCarrier
と PassengerCarrier
を別のエンティティとして用意します。
まだ4つのクラスがあるので、大したことはしていないように見えるかもしれませんが、これをスケールアップして想像してみてください。nPlaneクラスがあっても、
CargoCarrierと
PassengerCarrier` だけがあり、これらの飛行機に適用することができるのです。
さらに良い方法は、 Carrier
と Plane
という親クラスを持つことです。親クラスである Carrier
に対して、 Cargo
と Passenger
という2つの子クラスを作成することができます。同様に、 Plane
の親クラスには、 Military
と Commercial
という 2 つの子クラスを作成します。
次に、Carrier
と Plane
のサブクラスを接続する、つまりブリッジする方法が必要になります。これは、これら2つのクラスのうちの1つを、もう1つのクラスのコンストラクタのパラメータ値として渡すことで実現できます。このパターンを実装することで、どのサブクラスも組み合わせることができるようになります。
最後に、PythonでBridge Design Patternをどのように実装するか見てみましょう。
Pythonでブリッジデザインパターンを実装する
先ほども言ったように、carry_military()
と carry_passenger()
という二つの抽象メソッドを持つ親クラス Carrier
を作成します。次に、Carrier
クラスを継承して、 carry_military()
と carry_commercial()
のメソッドを実装した子クラス Cargo
を作成します。
クラスのバリエーションを増やすという滑り台を避けるために、 Carrier
を定義して、 carry_military()
と cary_passenger()
という二つの抽象的なメソッドを持たせることにしましょう。
Carrierクラスは、さらに
Cargoと
Passenger` という二つの子クラスを持ち、どちらもその抽象的なメソッドを継承して実装します。
# Passenger & Cargo Carriers
class Carrier:
def carry_military(self, items):
pass
def carry_commercial(self, items):
pass
class Cargo(Carrier):
def carry_military(self, items):
print("The plane carries ", items," military cargo goods")
def carry_commercial(self, items):
print("The plane carries ", items," commercial cargo goods")
class Passenger(Carrier):
def carry_military(self, passengers):
print("The plane carries ", passengers , " military passengers")
def carry_commercial(self, passengers):
print("The plane carries ", passengers , " commercial passengers")
同じように、 display_description()
と add_objects()
という 2 つの抽象メソッドを持つ Plane
クラスと、 Commercial
と Military
という 2 つの子クラスも作成します。Planeクラスのコンストラクタに
Carrier` を渡すことにします。これがブリッジになります。
飛行機が Commercial
であれば、 Cargo
と Passenger
は carry_commercial()
を返し、その逆も同様である。
乗客/荷物の数は self.objects
変数に格納され、 carry_commercial()
メソッドにパラメータとして渡されます。
# Military & Commercial Planes
class Plane:
def __init__(self, Carrier):
self.carrier = Carrier
def display_description(self):
pass
def add_objects(self):
pass
class Commercial(Plane):
def __init__(self, Carrier, objects):
super().__init__(Carrier)
self.objects = objects
def display_description(self):
self.carrier.carry_commercial(self.objects)
def add_objects(self, new_objects):
self.objects += new_objects
class Military(Plane):
def __init__(self, Carrier, objects):
super().__init__(Carrier)
self.objects = objects
def display_description(self):
self.carrier.carry_military(self.objects)
def add_objects(self, new_objects):
self.objects += new_objects
クラスは準備万端です。さて、いよいよオブジェクトを作成して、前述したコンストラクタの呼び出しによってオブジェクト間の橋渡しをする番です。
では、例を見てみましょう。
cargo = Cargo()
passenger = Passenger()
# Bridging Military and Cargo classes
military1 = Military(cargo , 100)
military1.display_description()
military1.add_objects(25)
military1.display_description()
ここでは、Cargo
と Passenger
クラスのオブジェクトをインスタンス化しました。そして、Military
クラスのコンストラクタ呼び出しで、Cargo
のインスタンスを渡しました。軍用機なので、貨物は軍用貨物とみなされます。
したがって、display_description()
メソッドは軍用貨物に関する詳細を出力します。さらに、このロードの上に、さらに 25
オブジェクトを追加しています。
The plane carries 100 military cargo goods
The plane carries 125 military cargo goods
同じように、Military
とPassenger
のクラスもブリッジすることができます。
cargo = Cargo()
passenger = Passenger()
# Bridging Military and Passenger classes
military2 = Military(passenger , 250)
military2.display_description()
military2.add_objects(10)
military2.display_description()
当然ながら、display_description()
メソッドには今乗っている軍人の人数が表示されます。
The plane carries 250 military passengers
The plane carries 260 military passengers
同様に、Commercial
とPassenger
を橋渡しすることができます。
# Bridging Commercial and Passenger
commercial1 = Commercial(passenger , 400)
commercial1.display_description()
commercial1.add_objects(50)
commercial1.display_description()
という結果になります。
The plane carries 400 commercial passengers
The plane carries 450 commercial passengers
そして最後に、Commercial
とCargo
のクラスをブリッジすることができます。
# Bridging Commercial and Cargo
commercial2 = Commercial(cargo, 150)
commercial2.display_description()
commercial2.add_objects(15)
commercial2.display_description()
となり、その結果は
The plane carries 150 commercial cargo goods
The plane carries 165 commercial cargo goods
結論
ブリッジデザインパターンは、抽象化と実装を分離する構造的なデザインパターンである。この記事では、Bridge Design Patternの背景となる動機とその仕組みについて探りました。
その後、Pythonで実装し、このパターンがどのように機能するかを紹介しました。