NullObjectパターン(PLoPD)
参考書籍一覧(Amazon アソシエイトリンク)
あるクラスと同じインターフェースを持つが何もしないクラスを用意するパターン。スペシャルケース(SpecialCase)パターンの一種。
たとえば、オークションサイトで買い手がいたら、買い手に通知をする処理を実装しているのだとする。
def notify_buyer_message(item, message)
return if item.buyer.nil?
item.buyer.notice(message)
end
買い手がいない場合、item.buyer
はnil
になるため、nullチェックが必要になる。以下のようにItem
とNullBuyer
クラスを定義することでnullチェックが不要になる。
def notify_buyer_message(item, message)
item.buyer.notice(message)
end
class Item
def buyer
@buyer ||= NullBuyer.new
end
end
class Buyer
def notice(message)
# 通知処理...
end
end
class NullBuyer < Buyer
def notice(message)
# 何もしない。必要なら成功時の戻り値を返す
end
end
継承よりもコンポジション(委譲)でも書いたが、NullObjectパターンは脆いクラス構造だと考えている。基底クラスにメソッドが追加された場合、NullObjectクラスにもメソッドを追加しなければならないが、追加を忘れてもオーバーライドしていないだけなのでエラーにはならない。したがって、NullObjectクラスを作るのであれば、継承関係を持たない同じインターフェースを実装したクラスとして実装した方が良いと考えている。
もっといえば、可能ならNullObjectクラスを作らず、存在しないケースも含めた1つのクラスとしてしまった方がよい。
class Buyer
# void_buyerフラグがtrueならNullObjectとして振る舞う
def initialize(name, void_buyer: false)
# ...
@void_buyer = void_buyer
end
def notice(message)
return if @void_buyer
# 通知処理...
end
end
フラグによらず、値によってNullObjectとして振る舞うべきか決めることもできる。