週末!プログラミング部

ソフトウェア開発ネタを中心に自分でいろいろ調べた内容を自分の勝手な解釈で思うがままに書いくためのブログ。サンプルソースコード、API、プラットフォーム、プログラミング言語、開発環境などを調査、分析して追求いく予定です。

Pythonでデザインパターン 玖ノ型「Bridge」

今回はPythonの勉強しながらデザインパターンのBridgeをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

Bridgeとは

直訳すると「橋」ですね。
wikiには「"橋渡し"のクラスを用意することによって、クラスを複数の方向に拡張させることを目的とする」とあります。

このパターンは、オブジェクトとオブジェクトの間にワンクッション挟むイメージです。
JavaC#みたいにインタフェース機能がある言語だとあまり使わない気もしますね(;´瓜`)

Bridgeについて考えてみる

いつものごとくいい例が思いつきませんが、以下のようなインタフェースがあったとします。

from abc import ABCMeta, abstractmethod

class Hoge(metaclass=ABCMeta) :
    @abstractmethod
    def functionA(self) :
        pass

普通にこのインタフェースを使おうとするとこんな感じでしょうか?

class Foo(Hoge) :
    def functionA(self) :
        print("機能A")

ここまではよく見かける書き方ですね では、このインタフェースを実装したクラスに手を加えずに機能拡張して"機能Aを3回実行"するようにしたいと思います。
単純に考えればこんな感じでしょうか?

if __name__ == "__main__" :
    foo = Foo()
    
    for num in range(3):
        foo.functionA()

これでもよいですがユーザ視点から考えると毎回functionA()を3回実行する処理を書くのは手間ですね。。。
そこで以下のようなクラスを用意します。

class Bar(Hoge) :
    def __init__(self, hoge):
        self._hoge = hoge

    def functionA(self) :
        for num in range(3):
            self._hoge.functionA()

これを使えば以下のようにすることができます。
fooインスタンスをそのまま使えば通常機能となり、Barオブジェクトに入れて実行すれば拡張機能となりますね(・`ω´・)v

if __name__ == "__main__" :
    foo = Foo()
    foo.functionA()    # 通常機能

    bar = Bar(foo)
    bar.functionA()    # 拡張機能

サンプルコード

今回はポケモンでピカピカ言っているアイツを倒してみたいと思います(๑◕ܫ◕๑)
このサンプルでは技のダメージをBridgeパターンで"効果抜群だ"と"効果は今ひとつだ"に拡張しています。

# coding: utf-8

from abc import ABCMeta, abstractmethod

# 技クラス
class Skill(metaclass=ABCMeta) :
    def __init__(self, name : str, type : str) :
        self.__name = name
        self.__type = type
    
    def getName(self) -> str:
        return self.__name
    
    def getType(self) -> str:
        return self.__type
    
    @abstractmethod
    def getDamege(self) :
        pass

# 効果抜群クラス
class superEffective(Skill):
    def __init__(self, skill) :
        self._skill = skill

    def getDamege(self):
        print("効果抜群だ!!")
        return self._skill.getDamege() * 1.5

# 効果は今ひとつクラス
class notVeryEffective(Skill):
    def __init__(self, skill) :
        self._skill = skill

    def getDamege(self):
        print("効果は今ひとつのようだ...")
        return self._skill.getDamege() * 0.75

# 技:地震
class earthquake(Skill):
    def __init__(self):
        super().__init__("じしん", "じめん")

    def getDamege(self):
        return 100

# 技:たいあたり
class taiatari(Skill):
    def __init__(self):
        super().__init__("たいあたり", "ノーマル")

    def getDamege(self):
        return 20

# 技:かぜおこし
class gust(Skill):
    def __init__(self):
        super().__init__("かぜおこし", "ひこう")

    def getDamege(self):
        return 40


class Pikachu(object):
    def __init__(self, name, hp):
        self.__name = name
        self.__hp = hp

    def Damege(self, skill):
        if skill.getType() == 'じめん':
            damege = superEffective(skill).getDamege()

        elif skill.getType() == 'ひこう':
            damege = notVeryEffective(skill).getDamege()

        else:
            damege = skill.getDamege()

        self.__hp = self.__hp - damege
        
        if self.__hp <= 0 :
            print(self.__name, "は たおれた")
            print("め の まえ が まっしろ に なった")

if __name__ == "__main__" :
    
    skill_taiatari = taiatari()
    skill_gust = gust()
    skill_earthquake = earthquake()
    
    pikachu = Pikachu(name = "ピカチュウ", hp = 75)
    
    # たいあたりしてみる
    print("------------")
    print(skill_taiatari.getName(), "を つかった")
    pikachu.Damege(skill_taiatari)
    
    # かぜおこしを使ってみる
    print("------------")
    print(skill_gust.getName(), "を つかった")
    pikachu.Damege(skill_gust)
    
    # じしんを使ってみる
    print("------------")
    print(skill_earthquake.getName(), "を つかった")
    pikachu.Damege(skill_earthquake)

実行結果は以下のとおりです!
ちゃんと倒せましたか?

$ python Brigge.py
------------
たいあたり を つかった
------------
かぜおこし を つかった
効果は今ひとつのようだ...
------------
じしん を つかった
効果抜群だ!!
ピカチュウ は たおれた
め の まえ が まっしろ に なった