かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Ruby Classメソッドのアクセス権

権限の種類

  • public ... どこからでもアクセス可能。クラス内にメソッドを定義した際のデフォルトのアクセス権
  • protected ... そのクラスまたはサブクラスのインスタンスメソッドからしか呼び出すことができない
  • private ... レシーバーの省略形でしか呼び出すことができない ( self に対してしか呼び出すことができない)

権限の設定方法

  1. public, protected,private` のキーワードに続けてメソッド名を文字列またはシンボルで引き渡す
    ※ 先にメソッドが定義されている必要がある
  2. 引数なしの public, protected,private` キーワードを置くと、それ以降のメソッドはその権限になる
class MyClass
  # デフォルトでは public メソッド
  def public_method
    p 'public'
  end

  protected
  # これより下は protected になる
  def protected_method
    p 'protected'
  end

  private
  # これより下は private になる
  def private_method
    p 'private'
  end

  def call_protected_method
    protected_method
  end

  def call_private_method
    private_method
  end
  # public メソッドに指定
  public :call_protected_method, :call_private_method
end

c = MyClass.new
c.public_method # => "public"
# protected メソッドは外部から直接呼び出すことはできない
c.protected_method
# => NoMethodError protected method `protected_method' called
# private メソッドも外部から直接呼び出すことはできない
c.private_method
# => NoMethodError private method `private_method' called
c.call_protected_method # => "protected"
c.call_private_method   # => "private"

Protected と Private 権限の違い

protected はメソッドが定義されているクラスと、そのサブクラスのインスタンスメソッドから呼び出すことができる
privateself に対してのみ呼び出しが可能、つまり同じクラスのオブジェクトでも他のオブジェクトの持つ private メソッドは呼び出すことができない

Protected

class ClassPermission
  attr_reader :name
  def initialize(name)
    @name = name
  end

  def use_protected(recever = self)
    print "#{@name} call #{recever.name}.protected_method > "
    recever.protected_method
  end

  protected
  def protected_method
    p 'protected'
  end
end

# サブクラス
class SubClassPermission < ClassPermission
end

c1 = ClassPermission.new('C1')
c2 = ClassPermission.new('C2')
cSub = SubClassPermission.new('SubClass')

# 自身をレシーバーにじて protected メソッドを実行できる
c1.use_protected(c1)
# => C1 call C1.protected_method > "protected"

# 同じクラスインスタンスからレシーバーを通じて protected メソッドを実行できる
c2.use_protected(c1)
# => C2 call C2.protected_method > "protected"

# サブクラスのインスタンスからレシーバーを通じて protected メソッドを実行できる
cSub.use_protected(c1)
# => SubClass call C1.protected_method > "protected"

無関係なクラスインスタンスのメソッド内からはレシーバーを通じても呼び出すことはできない

class Hoo
  def use_protected(recever = self)
    puts "Hoo call #{recever.name}.protected_method"
    recever.protected_method
  rescue => e
    p e
  end
end

hoo = Hoo.new
hoo.use_protected(c1)
# => Hoo call C1.protected_method
# => NoMethodError: protected method `protected_method' called

Private

class ClassPermission
  attr_reader :name
  def initialize(name)
    @name = name
  end

  def call_private
    print "#{@name} call private_method > "
    private_method
  end

  def use_private(recever = self)
    puts "#{@name} call #{recever.name}.private_method > "
    recever.private_method
  end

  def call_recever_call_private(recever = self)
    puts "#{@name} call #{recever.name}.call_private >> "
    recever.call_private
  end

  private
  def private_method
    p 'private'
  end
end

c1 = ClassPermission.new('C1')
c2 = ClassPermission.new('C2')
class SubClassPermission < ClassPermission
end

c1.call_private
# => C1 call private_method > "private"

# private メソッドは self の省略形でしか呼び出すことができない
c1.use_private
# => C1 call C1.private_method >
# => NoMethodError: private method `private_method' called

# 当然他のクラスインタンスからレシーバーを通じて呼び出そうとしてもエラー
c2.use_private(c1)
# => C2 call C1.private_method >
# => NoMethodError: private method `private_method' called

# レシーバー自身の private メソッドを使っているメソッドを呼び出すのはOK
c1.call_recever_call_private(c1)
# => C1 call C1.call_private >
# => C1 call private_method > "private"

c2.call_recever_call_private(c1)
# => C2 call C1.call_private >>
# => C1 call private_method > "private"

# サブクラスからでもOK
cSub.call_recever_call_private(c1)
# => SubClass call C1.call_private >>
# => C1 call private_method > "private"

private メソッドを呼び出しているメソッドが public メソッドなら無関係なクラスからでも呼び出せる

class Hoo
  def call_recever_call_private(recever = self)
    puts "Hoo call #{recever.name}.call_private >> "
    recever.call_private
  end
end

hoo = Hoo.new
hoo.call_recever_call_private(c1)
# => Hoo call C1.call_private >>
# => C1 call private_method > "private"

private メソッドを呼び出しているメソッドが protected なら無関係のクラスのインスタンスメソッドからは呼び出せなくなる

class ClassPermission
  attr_reader :name
  def initialize(name)
    @name = name
  end

  protected
  def call_private
    print "#{@name} call private_method > "
    private_method
  end

  private
  def private_method
    p 'private'
  end
end

class Hoo
  def call_protected_recever_call_private(recever = self)
    puts "Hoo call #{recever.name}.call_private >> "
    recever.call_private
  rescue => e
    p e
  end
end

c1 = ClassPermission.new('c1')
hoo = Hoo.new
hoo.call_protected_recever_call_private(c1)
# => Hoo call c1.call_private >>
# => NoMethodError: protected method `call_private' called

まとめ

Ruby のクラスインスタンスメソッドの権限は public, protected, private があり、設定方法は2種類ある。
最もアクセス権が厳しいのが private で自身(self)からしか呼び出すことができない
protected は同じクラスのインスタンスだけでなく、子孫クラスのインスタンスメソッドからも呼び出すことができる


プログラミング言語 Ruby

プログラミング言語 Ruby