週末!プログラミング部

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

Pythonでデザインパターン 捌ノ型「AbstractFactory」

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

AbstractFactoryとは

直訳すると「抽象的な工場」になります。
wikiには「関連するインスタンス群を生成するための API を集約することによって、複数のモジュール群の再利用を効率化することを目的とする」とあります。

AbstractFactoryについて考えてみる

突然だが醤油ラーメンを作ろうと思う(・`ω´・;)

# スープを作る
soup = Soup(SoySauce()) # 醤油ベース

# 麺を作る
noodle = Noodle(hardness="H", thickness=5) # 硬め、やや太麺

# トッピングを用意
topping = Topping()
topping.add(PorkFillet())   # チャーシュー
topping.add(Leek()) # ネギ

# ラーメン鉢を用意して盛り付け
ramenBowl = RamenBowl()
ramenBowl.add(soup)
ramenBowl.add(noodle)
ramenBowl.add(topping)

一般的な醤油ラーメンの出来上がりだ!
これが一般的な醤油ラーメンかはさておき(・`ω´・;)

でも別の人が作ればこうならないかもしれない。
例えば醤油ベースのスープに豚骨ラーメンのトッピングをしてしまうかも(;^ω^)

# 豚骨ラーメンのトッピングを用意
topping = Topping()
topping.add(Tororo())   # とろろ
topping.add(Kelp())     # 昆布
topping.add(Bonito())   # かつお節

お客がラーメン鉢の中に好き勝手盛りできるセルフサービスラーメン屋ならいいけれど、 お客からの注文を受けて店員が間違えて盛り付けようならクレーム必至だろう(;゚Д゚)
だから、そうならないようにするために以下のような醤油ラーメンクラスを用意してみました!

class SoySauceRamen(object) :
    def __init__(self) :
        # スープ
        self.soup = Soup(SoySauce()) # 醤油ベース

        # 麺
        self.noodle = Noodle(hardness="H", thickness=5)

        # トッピング
        self.topping = Topping()
        self.topping.add(PorkFillet())   # チャーシュー
        self.topping.add(Leek()) # ネギ

    def getSoup(self) -> Soup :
        return self.soup

    def getNoodle(self) -> Noodle :
        return self.noodle

    def getTopping(self) -> Topping :
        return self.topping

if __name__ == "__main__" :
    # 醤油ラーメンの元
    soySauceRamen = SoySauceRamen()
    
    # ラーメン鉢を用意して盛り付け
    ramenBowl = RamenBowl()
    ramenBowl.add(soySauceRamen.getSoup())
    ramenBowl.add(soySauceRamen.getNoodle())
    ramenBowl.add(soySauceRamen.getTopping())

同じように豚骨ラーメンを作るときはまた専用のクラスを用意しておけば材料を間違わないね!!

class PigBonesRamen(object) :
    def __init__(self) :
        # 材料準備処理は割愛

    def getSoup(self) -> Soup :
        return self.soup

    def getNoodle(self) -> Noodle :
        return self.noodle

    def getTopping(self) -> Topping :
        return self.topping

if __name__ == "__main__" :
    pigBonesRamen = PigBonesRamen()
    
    # ラーメン鉢を用意して盛り付け
    ramenBowl = RamenBowl()
    ramenBowl.add(pigBonesRamen.getSoup())
    ramenBowl.add(pigBonesRamen.getNoodle())
    ramenBowl.add(pigBonesRamen.getTopping())

ここまでくればお気づきかもですが醤油ラーメンクラスと豚骨ラーメンクラスで共通部分をまとめられそうです。

class RamenBase(metaclass=ABCMeta) :
    @abstractmethod
    def getSoup(self) -> Soup :
        pass

    @abstractmethod
    def getNoodle(self) -> Noodle :
        pass

    @abstractmethod
    def getTopping(self) -> Topping :
        pass

そして以下のようなクラスを用意すればユーザは好きなラーメンの材料を手に入れることができ、盛り付けミスしなくなりますね(・`ω´・)b

class RamenFactory(object) :
    deg getRamen(ramenType : str) -> RamenBase :
        if ramenType == "PigBonesRamen" :
            return PigBonesRamen()
            
        elif ramenType == "MisoRamen" :
            return MisoRamen()
            
        elif ramenType == "SaltRamen" :
            return SaltRamen()
            
        else :
            return SoySauceRamen()

こんな感じがAbstractFactoryだと思っています

サンプルコード

抽象的に書いたのでこのままでは動きませんがサンプルコードです。

class RamenBase(metaclass=ABCMeta) :
    @abstractmethod
    def getSoup(self) -> Soup :
        pass

    @abstractmethod
    def getNoodle(self) -> Noodle :
        pass

    @abstractmethod
    def getTopping(self) -> Topping :
        pass

class SoySauceRamen(RamenBase) :
    # 処理内容は割愛

class PigBonesRamen(RamenBase) :
    # 処理内容は割愛

class MisoRamen(RamenBase) :
    # 処理内容は割愛

class SaltRamen(RamenBase) :
    # 処理内容は割愛

class RamenFactory(object) :
    deg getRamen(ramenType : str) -> RamenBase :
        if ramenType == "PigBonesRamen" :
            return PigBonesRamen()
            
        elif ramenType == "MisoRamen" :
            return MisoRamen()
            
        elif ramenType == "SaltRamen" :
            return SaltRamen()
            
        else :
            return SoySauceRamen()

if __name__ == "__main__" :
    # ラーメン工場を設立
    ramenFactory = RamenFactory()
    
    # 豚骨ラーメンの注文が入ったら?
    ramen = ramenFactory.getRamen("PigBonesRamen")
    
    # ラーメン鉢を用意して盛り付け
    ramenBowl = RamenBowl()
    ramenBowl.add(ramen.getSoup())
    ramenBowl.add(ramen.getNoodle())
    ramenBowl.add(ramen.getTopping())

    # へい、お待ち!!