いきなりGOF外のデザインパターン
けど、よく使いそうな感じだったのでメモ
プログラムデザインのためのパターン言語 が初出
どんなパータン?
よくあるこんな書き方
obj.method() if obj
あるオブジェクトが nil でなければ、メソッドを呼び出す
こういうパターンが頻出する場合、obj に nil の代わりに何もしないメソッドを持つオブジェクト を格納しておく
インターフェースだけ持って、何もしないオブジェクトを用意することで、
そのオブジェクトを使う側は、オブジェクトが生成されなかった場合を気にする必要がなくなる
→ オブジェクトの状態を意識する必要が無い
→ 結合度が下がる
実装例
Factoryパターンとの組み合わせで、インスタンスの生成が行えない場合、NullObject を返す
module Animal
class Cat
def sound
puts 'meow'
end
end
class Dog
def sound
puts 'wow'
end
end
class NilAnimal # NullObject
def sound
end
end
def self.create(type)
Animal.const_get(type.to_s.capitalize).new rescue NilAnimal.new
end
end
[:cat, :dog, :llama].map do |type|
Animal.create(type)
end.each(&:sound)
これにより、Animal.create を使う側は、Animal.create が nil を返す場合を意識する必要がなくなる
- 今回ならば、
each(&:sound)
の所を、each{|animal| animal.sound if animal }
と書かなくて済む
リファクタリング案
NullObject は、複数生成する必要はないので、Singleton にする場合が多い
require 'Singleton'
module Animal
...
class NilAnimal
include Singleton
def sound
end
end
def self.create(type)
Animal.const_get(type.to_s.capitalize).new rescue NilAnimal.instance
end
end
リファクタリング案2
個人的には、汎用の NilObject class を1つ作っちゃっても良いと思ってる
- Animal class に無いメソッドを呼ばれるのが困る場合は、別途 Animal::NilAnimal を作るべき
require 'Singleton'
class NilObject < BasicObject
include ::Singleton
def method_missing(*args) # どんなメソッドが呼ばれても、nil を返す
end
def respond_to_missing?(*args)
true
end
end
module Animal
class Cat
def sound
puts 'meow'
end
end
class Dog
def sound
puts 'wow'
end
end
def self.create(type)
Animal.const_get(type.to_s.capitalize).new rescue NilObject.instance
end
end
[:cat, :dog, :llama].map do |type|
Animal.create(type)
end.each(&:sound)
Rails4 以降なら Object#try が使える
require "rubygems"
require "active_support/core_ext"
module Animal
class Cat
def sound
puts 'meow'
end
end
class Dog
def sound
puts 'wow'
end
end
def self.create(type)
Animal.const_get(type.to_s.capitalize).new rescue nil
end
end
[:cat, :dog, :llama].map do |type|
Animal.create(type).try(:sound)
end
Object#try は、第一引数を public_send して、結果が取れればその結果を、
NoMethodError ならば、nil を返すメソッド
ただし、Rails 3.x までは、NoMethodError を返すので注意
参考
基本的に使へるなら使ふべきパターン。特に、「if 文で null かどうか判定して null でなければメソッドを呼び出す」の様なパターンがいくつもある場合は設計が怪しいので null object パターンを真剣に検討した方が良い。