end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

GoF Decorator for python

pythonにdecorator機能というものがあることを知りました。

pythonのdecorator機能とは、 「関数やクラスの前後に特定の処理を追加できる」ようですが、 GoFのデコレータパターンとは異なるようです。

で、pythonのdecorator機能を試す前に、 GoFデザインパターンの振り返りとして、 以下の「レベルアップJavaデザインパターン編~」を pythonで実装します。

https://www.amazon.co.jp/dp/B086D2HLKK

実装例

以下の「ミックスアイス」で理解できると思います。

クラス図

┌IceCream(Abstract Class)─────┐
├─────────────────┤
│+get_price():int(Abstract Method) ├←┐
│+get_name() :str(Abstract Method) │  │
└───┬────────┬────┘  │
        △                △           集約
       継承              継承           ◇
┌Corn─┴────┐┌Ice ┴──────┴┐
│-price:int      ││#ice_cream:IceCream │
│-name:str       ││                    │
├────────┤├──────────┤
│+get_price():int││+get_price():int    │
│+get_name() :str││+get_name() :str    │
└────────┘└─┬──┬─────┘
                        △    △               
                       継承  継承      
              ┌Vanilla ┴┐┌┴Chocolate ┐
              │-price:int││-price:int  │
              │-name:str ││-name:str   │
              ├─────┤├──────┤
              └─────┘└──────┘    

python script

# -*- coding: utf-8 -*-
import abc # Abstract Base Classes モジュール

def main():
    corn = Corn()
    vanilla = Vanilla(corn)
    choco   = Chocolate(corn)
    mix     = Chocolate(Vanilla(corn))
    
    print(vanilla.get_name(),vanilla.get_price() )
    print(choco.get_name(),  choco.get_price() )
    print(mix.get_name(),    mix.get_price() )

class IceCream(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get_price(self) -> int:
        raise NotImplementedError()
    
    @abc.abstractmethod
    def get_name(self) -> str:
        raise NotImplementedError()
    
class Corn(IceCream):
    def __init__(self):
        self.price = 100
        self.name  = "アイスクリーム"

    def get_price(self) -> int:
        return self.price
    
    def get_name(self) -> str:
        return self.name

class Ice(IceCream):
    def __init__(self,ice_cream):
        self.ice_cream = ice_cream
        
    def get_price(self) -> int:
        return self.price + self.ice_cream.get_price()
    
    def get_name(self) -> str:
        return self.name + self.ice_cream.get_name()
    
class Vanilla(Ice):
    def __init__(self,ice_cream):
        super().__init__(ice_cream)
        self.price = 100
        self.name  = "バニラ"
    
class Chocolate(Ice):
    def __init__(self,ice_cream):
        super().__init__(ice_cream)
        self.price = 70
        self.name  = "チョコ"
    
if __name__ == '__main__':
    main()

↑こう書くと↓こう表示されます

>python foo.py
バニラアイスクリーム 200
チョコアイスクリーム 170
チョコバニラアイスクリーム 270