Pythonによるブリッジデザインパターン

Bridge Design Patternは構造的デザインパターンであり、抽象化と実装を分離するものです。この記事では、PythonにおけるBridge Design Patternの動機と実装について説明します。

デザインパターンとは、ソフトウェアエンジニアリングにおける一般的なアーキテクチャ上の問題に対する標準化されたプラクティスや解決策の集合を指します。

ブリッジのデザインパターンに隠された動機

Bridgeパターンは、いわゆるデカルト積の複雑性の爆発を防ぐものである。

この問題は、例によって明らかになる。例えば、あなたが飛行機を実装しているとしよう。それは軍用機であったり、商用機であったりする。さらに、旅客機・兵員機・貨物機であることもできます。

これを実装する一つの方法は、MilitaryPassengerMilitaryCargoCommercialPassengerCommercialCargoという飛行機を持たせることです。

ここで、デカルト積の複雑さは 2 x 2 = 4 となります。この規模ではこの数字は画期的なものではありませんが、クラスやバリエーションを増やすと指数関数的に増加し、あっという間に手に負えなくなってしまうでしょう。

ブリッジパターンは、クラス(Airplaneの実装)とその特性(旅客機なのか貨物機なのか)の間の橋渡しとして使われます。このパターンでは、継承よりもコンポジションを優先します。

このパターンを使うと、異なるカテゴリの型ごとに1つのクラスを作成することになります。例えば、この例では、 CommercialPlaneMilitaryPlane をエンティティとして、 CargoCarrierPassengerCarrier を別のエンティティとして用意します。

まだ4つのクラスがあるので、大したことはしていないように見えるかもしれませんが、これをスケールアップして想像してみてください。nPlaneクラスがあっても、CargoCarrierPassengerCarrier` だけがあり、これらの飛行機に適用することができるのです。

さらに良い方法は、 CarrierPlane という親クラスを持つことです。親クラスである Carrier に対して、 CargoPassenger という2つの子クラスを作成することができます。同様に、 Plane の親クラスには、 MilitaryCommercial という 2 つの子クラスを作成します。

次に、CarrierPlane のサブクラスを接続する、つまりブリッジする方法が必要になります。これは、これら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クラスは、さらにCargoPassenger` という二つの子クラスを持ち、どちらもその抽象的なメソッドを継承して実装します。

# 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 クラスと、 CommercialMilitary という 2 つの子クラスも作成します。PlaneクラスのコンストラクタにCarrier` を渡すことにします。これがブリッジになります。

飛行機が Commercial であれば、 CargoPassengercarry_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()


ここでは、CargoPassenger クラスのオブジェクトをインスタンス化しました。そして、Military クラスのコンストラクタ呼び出しで、Cargo のインスタンスを渡しました。軍用機なので、貨物は軍用貨物とみなされます。

したがって、display_description() メソッドは軍用貨物に関する詳細を出力します。さらに、このロードの上に、さらに 25 オブジェクトを追加しています。

The plane carries 100 military cargo goods
The plane carries 125 military cargo goods


同じように、MilitaryPassengerのクラスもブリッジすることができます。

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


同様に、CommercialPassengerを橋渡しすることができます。

# 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


そして最後に、CommercialCargoのクラスをブリッジすることができます。

# 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で実装し、このパターンがどのように機能するかを紹介しました。

タイトルとURLをコピーしました