LoginSignup
0
0

More than 5 years have passed since last update.

[Ruby入門] 10. クラスを定義する② メソッドと変数へのアクセスを制御する

Last updated at Posted at 2017-04-16

>> 連載の目次は こちら!

クラスの話の2回目は、クラスのメソッドと変数に対するアクセス制御について整理してみる

■ メソッドへのアクセス制御

[参考URL]
http://qiita.com/tbpgr/items/6f1c0c7b77218f74c63e

●概要

  • メソッドに対して、 public / protected / private というキーワードを指定して、アクセス可能な範囲を指定する
  • 指定しなければ public になるが、initializeメソッドだけはprivateになる
  • public / protected / private の意味は以下

[public]
普通に、オブジェクトからインスタンスメソッドとして実行可能。
initializeメソッド以外のデフォルト。

[protected]
そのメソッドを持つオブジェクトが self であるコンテキスト(メソッド定義式や instance_eval )でのみ呼び出せる
==> そのクラスおよびサブクラスの中からしか呼び出せなくなる。

[private]
関数形式でしか呼び出せなくなる
==> レシーバを指定した呼び出し方( インスタンス.メソッド )ができなくなるため、インスタンスの外から実行できなくなる。
initializeメソッドは常にprivateになる。

●アクセス制御の指定方法

[シンボルで指定する]
class MyCls
  def my_protected1; end
  def my_protected2; end
  def my_private1; end
  def my_private2; end

  protected :my_protected1, :my_protected2
  private :my_private1, :my_private2
end
[ここからはprotectedです! みたいに指定する]
class MyCls
  protected
  def my_protected1; end
  def my_protected2; end

  private
  def my_private1; end
  def my_private2; end
end

●アクセス制御を確認してみる

class MyCls

  # 確認用publicメソッド
  def test
    # 同一クラス内から self をレシーバとして public なメソッドを実行できる
    puts 'inner MyCls: self.my_public ==> ' + self.my_public

    # 同一クラス内から レシーバを省略して public なメソッドを実行できる
    puts 'inner MyCls: my_public ==> ' + my_public

    # 同一クラス内から self をレシーバとして protected なメソッドを実行できる
    puts 'inner MyCls: self.my_protected ==> ' + self.my_protected

    # 同一クラス内から レシーバを省略して protected なメソッドを実行できる
    puts 'inner MyCls: my_protected ==> ' + my_protected

    # クラス内で self をレシーバとして private なメソッドを実行...できない
    #   Uncaught exception: private method `my_private' called for 〜
    self.my_private

    # 同一クラス内から レシーバを指定せずに private なメソッドを実行できる
    puts 'inner MyCls: my_private ==> ' + my_private

  end

  def my_public; __method__.to_s end

  protected
  def my_protected; __method__.to_s end

  private
  def my_private; __method__.to_s end
end

class SubClass < MyCls
  # サブクラスからの確認用publicメソッド
  def test
     # サブクラスから self をレシーバとして 親クラスの public なメソッドを実行できる
    puts 'from SubCls: self.my_public ==> ' + self.my_public

    # サブクラスから レシーバを省略して 親クラスの public なメソッドを実行できる
    puts 'from SubCls: my_public ==> ' + my_public

    # サブクラスから self をレシーバとして 親クラスの protected なメソッドを実行できる
    puts 'from SubCls: self.my_protected ==> ' + self.my_protected

    # サブクラスから レシーバを省略して 親クラスの protected なメソッドを実行できる
    puts 'from SubCls: my_protected ==> ' + my_protected

    # クラス内で self をレシーバとして 親クラスの private なメソッドを実行...できない
    #   Uncaught exception: private method `my_private' called for 〜
    self.my_private

    # サブクラスから レシーバを指定せずに private なメソッドを実行できる
    puts 'from SubCls: my_private ==> ' + my_private
  end
end

mycls = MyCls.new
subcls = SubClass.new

# クラス内、サブクラス内からのアクセステスト
mycls.test; subcls.test

# クラスの外からのアクセステスト
puts mycls.my_public  # 普通にできる

puts mycls.my_protected  # できない
   # Uncaught exception: protected method `my_protected' called for 〜

puts mycls.my_private  # できない
   # Uncaught exception: private method `my_private' called for 〜

puts my_private  # もちろんできない(mainオブジェクトに対する定義を探しに行く)
   # Uncaught exception: undefined local variable or method `my_private' for main:Object

■ 変数へのアクセス制御

前回の記事で書いたとおり、インスタンス変数、クラス変数は、メソッドを通してしか外部からアクセスできない。

●インスタンス変数へのアクセス用メソッドを定義する

[明示的にアクセス用のメソッドを書く]
class MyCls
  # ゲッターに該当するメソッド
  def name
    @name
  end

  # セッターに該当するメソッド
  def name=(name)
    @name = name
  end
end

# こんなふうに使える
cls = MyCls.new
cls.name = 'miro'
puts cls.name
[アクセサを自動定義する]
  • 前述のようなお決まりのアクセサを、全部のインスタンス変数に手動で定義するのは大変だし、ソースが膨らんでしまう。
  • なので、attr_xxx というキーワード(Moduleクラスのメソッド)で、自動生成することができる。

[attr_accessor] インスタンス変数を読み書きするためのアクセサメソッドを定義
[attr_reader] インスタンス変数を読み出すためのアクセサメソッドを定義
[attr_writer] インスタンス変数を書き込むためのアクセサメソッドを定義

class MyCls
  attr_accessor :var1, :var2
  attr_reader   :var3, :var4
  attr_writer   :var5, :var6

  def initialize(val1, val2, val3, val4, val5, val6)
    @var1, @var2, @var3, @var4, @var5, @var6 = val1, val2, val3, val4, val5, val6
  end
end

# 確認してみる
cls = MyCls.new(1, 2, 3, 4, 5, 6)
cls.var1 = 11; puts cls.var1   # ゲッター/セッターがあるので、値のセットと取得ができる
puts cls.var3                  # ゲッターのみだから、cls.var3 = xxx とかはできない
cls.var5 = 55                  # セッターのみだから、puts cls.var5 とかはできない

●クラス変数へのアクセス用メソッドを定義する

前述のアクセサの自動定義(attr_xxx)は、クラス変数に対しては使えない。
なので、クラス変数へのアクセスは、明示的にメソッドを定義する。

# クラス変数を attr_xxx に指定してもだめ、なことを確認するためのクラス
class MyTestCls
  @@my_class_var = 999
  attr_accessor :my_class_var # こんなことしてもだめ

  def self.dump_class_var
    p @@my_class_var
  end
end

cls = MyTestCls.new
p cls                     # <MyTestCls:0x007f993d972038>
cls.my_class_var = 888    # 一見...
puts cls.my_class_var     # 888  読み書きできているように見えるが...
MyTestCls.dump_class_var  # 999  変わってない。
p cls                     # <MyTestCls:0x007f993d972038 @my_class_var=888> インスタンス変数が追加されているだけだな...


# クラス変数は明示的にメソッドを定義すればアクセスできる、ことを確認するためのクラス
class MyTestCls2
  @@my_class_var = 999

  def my_class_var
    @@my_class_var
  end
  def my_class_var=(val)
    @@my_class_var = val
  end

  def self.dump_class_var
    p @@my_class_var
  end
end

cls = MyTestCls2.new
p cls                     # <MyTestCls2:0x007fa4660c6720>
cls.my_class_var = 777    # 今度は...
puts cls.my_class_var     # 777 どうかな...
MyTestCls2.dump_class_var # 777 うん、クラス変数の値が変わってる
p cls                     # <MyTestCls2:0x007fa4660c6720> インスタンス変数が追加されたりとかしてない
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0