Rubyのprivate/protectedは特殊で誰しもが混乱する設計の一つです。
そんなprotectedの使い所について書かれています。
- 作者: Peter J.Jones
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/19
- メディア: Kindle版
- この商品を含むブログ (5件) を見る
項目14 protectedメソッドを使ってプライベートな状態を共有しよう
カプセル化が邪魔になるケース
他のオブジェクトの内部状態にアクセスしなければならない場合。
instance_eval使えばいけるけど、、、
class Widget # 他のobjectと座標が重なっているかチェックする場合など def overlapping?(other) x1, y1 = @screen_x, @screen_y x2, y2 = other.instance_eval { [@screen_x, @screen_y] } end end
protectedで解決
class Widget def overlapping?(other) x1, y1 = @screen_x, @screen_y x2, y2 = other.screen_coordinates end protected def screen_coordinates [@screen_x, @screen_y] end end
覚えておく事項
- プライベートな状態はprotectedメソッドで共有できる
- レシーバを明示してprotectedメソッドを呼び出せるのは、同じクラスのオブジェクトか共通のスーパークラスからprotectedメソッドを継承しているオブジェクトだけだ。
一応private/protectedのおさらい
privateはサブクラスからも呼べます。それってJavaでいうprotectedでは?と思いますが、
ここの違いは、インスタンス経由で呼べるかどうかです。
サブクラスのインスタンスから呼べるのがprotected、呼べないのがprivateと。
class Person protected def protected_method puts "Person#protected_method" end private def private_method puts "Person#private_method" end end class Runner < Person # ok(これはいいよね) def call_protected_method protected_method end # ok(これが呼べちゃう) def call_private_method private_method end # ok(インスタンスのprotectedなのでok) def call_instance_protected_method(instance) instance.protected_method end # ng(インスタンスのprivateなのでng) def call_instance_private_method(instance) instance.private_method end end >> r1 = Runner.new >> r2 = Runner.new >> r1.call_protected_method Person#protected_method >> r1.call_private_method Person#private_method >> r1.call_instance_protected_method(r2) Person#protected_method >> r1.call_instance_private_method(r2) NoMethodError (private method `private_method' called for #<Runner:0x00007fed1a8818f0>
まとめ
プライベートな状態はprotectedメソッドで共有できることと、 privateは明示的なレシーバを用いて呼び出せないと覚えておけば良い。