doc.dev1x.org

Null Object

1. 目的

処理が何も実行されないことを明示的に表現したい

class Program:
    def __init__(self, type):
        self._logic = Factory.create(type)

    def execute(self):
        return self._logic.execute()

if __name__=='__main__':
    for i in range(5):
        p = Program(i)
        result = p.execute()
        print("{}:{}".format(i, result))
class Factory:
    @staticmethod
    def create(self, type):
        if type == 1: return Logic01()
        if type == 2: return Logic02()
        raise ValueError('Error:Unknown Type')

2. 課題

想定外のパラメータを与えられてもエラーを起こさず処理を継続したい

3. 解決策

何もしないダミーオブジェクト(Null Object)を導入する

class Logic(metaclass=ABCMeta):
    @abstractmethod
    def execute(self):
        pass

class Logic01(Logic):
    def execute(self):
        # 何か複雑なロジックがあると考えること
        ...
        return "わーい"

class NullLogic(Logic):
    def execute(self):
        # 具体的なロジックは何も無い
        return "..."

4. メリット

エラーは起きず、余計な副作用も発生しない

利用側に特別な処理を強要する必要が無くなる

class Program:
    def __init__(self, type):
        try:
            self._logic = Factory.create(type)  # 未定義のtypeを渡すと例外が発生されると仮定
        except:
            self._logic = None

    def execute(self):
        if self._logic is None:
            return "..."
        else:
            return self._logic.execute()

if __name__=='__main__':
    for i in range(5):
        p = Program(i)
        result = p.execute()
        print("{}:{}".format(i, result))

5. デメリット

Null Objectを知らないと何をしているのか理解できない

6. 注意事項

使いどころが難しい

参考資料

Appendix-1 プログラム全体

class Program:
    def __init__(self, type):
        self._logic = Factory.create(type)

    def execute(self):
        return self._logic.execute()

class Logic(metaclass=ABCMeta):
    @abstractmethod
    def execute(self):
        pass

class Logic01(Logic):
    def execute(self):
        return "わーい"

class Logic02(Logic):
    def execute(self):
        return "うぇーい"

class NullLogic(Logic):
    def execute(self):
        return "..."

class Factory:
    @staticmethod
    def create(self, type):
        if type == 1: return Logic01()
        if type == 2: return Logic02()
        return NullLogic()      # いずれの条件にもマッチしない場合はNullLogicを返却する

if __name__=='__main__':
    for i in range(5):
        p = Program(i)
        result = p.execute()
        print("{}:{}".format(i, result))
1:わーい
2:うぇーい
3:...
4:...
5:...