Kamuycikap - SentenceDataBase

日々の勉強の記録を気分で書き綴るブログ

クラスの作成

Rubyには非常に多くの組み込みクラスが用意されており、さらにRubyプログラムの中で追加のクラスを定義する事が出来る。

class ClassName
  # ...
end

クラスの名前はそれ自体がグローバル定数なので、大文字で始める必要がある。
クラス定義には、クラス定数、クラスメソッド、インスタンス変数、インスタンスメソッドを含める事ができる。
クラスデータは、クラスの全てのオブジェクトから利用できるのに対し、インスタンスデータは1つのオブジェクトからしか利用できない。

class Friend
  @@myname = "Fred"             # クラス変数

  def initialize(name, sex, phone)
    @name, @sex, @phone = name, sex, phone
    # これらはインスタンス変数
  end

  def hello                     # インスタンスメソッド
    print "Hi, I'm #{@name}.\n"
  end

  def Friend.our_common_friend  # クラスメソッド
    print "We are all friends of #{@@myname}.\n"
  end
end
f1 = Friend.new("Susan","F","555-0123")
f2 = Friend.new("Tom","M","555-4567")
f1.hello                     # Hi, I'm Susan.
f2.hello                     # Hi, I'm Tom.
Friend.our_common_friend     # We are all friends of Fred.

実行結果はこうなる。

Hi, I'm Susan.
Hi, I'm Tom.
We are all friends of Fred.

クラスレベルのデータは、クラス全体でアクセスできるため、クラスを定義する時点で初期化する事ができる。
メソッドinitializeは、インスタンスが割り当てられた直後に必ず実行される。
inisializeメソッドは、従来のコンストラクタの概念に似ているが、メモリ割り当てを処理する必要は無い。
割り当てはnewによって内部的に処理され、解放はガーベージコレクションにょって透過的に処理される。

次のサンプルを、getmyvar、setmyvar、myvarの各メソッドに注意しながら眺めてほしい

class MyClass
  
  NAME = "Class Name" # クラス定数
  @@count = 0         # 初期化が必要

  def initialize                # オブジェクトが割り当てられるときに呼び出される
    @@count += 1
    @myvar = 10
  end

  def MyClass.getcount          # クラスメソッド
    @@count                     # クラス変数
  end

  def getcount                  # クラス変数を返すインスタンス
    @@count
  end

  def getmyvar                  # インスタンスメソッド
    @myvar                      # インスタンス変数
  end

  def setmyvar(val)             # @myvarを設定するインスタンス
    @myvar = val
  end

  def myvar=(val)               # @myvarを設定する別の方法
    @myvar = val
  end
end

foo = MyClass.new               # @myvarは10
puts foo.setmyvar 20            # @myvarは20
puts foo.myvar = 30             # @myvarは30

この例では、getmyvarが@myvarの値を参照し、setmyvarがそれを設定している。
多くの参考書ではこのようなメソッドを「ゲッター」「フッター」と呼んでいる。
これらはきちんと機能するが、Ruby式の上手なやり方では無い。
myvar=メソッドは、代入のオーバーローディングのように見えるがそうではない。
setmyvarよりは良い代替手段ではあるが、最善の方法ではない。
Moduleクラスには、attr、attr_accessor、attr_reader、attr_writerと言うメソッドが含まれている。
これらを使うと、制御されたインスタンスデータへのアクセスを自動的に処理できる。

例として、上記サンプルコードの三つのメソッドを、クラス定義で次の様に定義する。

attr_accessor :myvar # パラメータとなるシンボル

これにより@myvarの値を返すmyvarメソッドと、同じ変数の設定を可能にするmyvar=メソッドが作成される。
attr_readerメソッドとattr_writerメソッドはそれぞれ属性の読み取り専用バージョンと書き込み専用バージョンを作成する。
オブジェクトメソッドの中では、必要に応じて擬似変数selfを使用できる。
これは、現在のデフォルトのレシーバ・・・つまり実行中のインスタンスメソッドが属するオブジェクトへの参照にすぎない。

private、protected、publicの各メソッドを使うと、クラスのメソッドの可視性を制御できる。
インスタンスは常にprivateで、アクセサを用いる以外はクラスの外部からはアクセスできない。

これらの可視性の変更宣言は、:fooなどのシンボルをパラメータとしてとる。
パラメータを省略すると、クラスのそれ以降の全ての定義に可視性が適用される。

class MyClass
  def method1
    # ...
  end

  def method2
    # ...
  end

  def method3
    # ...
  end

  private :method1
  public :method2
  protected : method3

  private

  def my_method
    # ...
  end

  def another_method
    # ...
  end
end

上記の例では、method1がprivate、method2がpublic、method3がprotectedとなる。
その後ろのprivateメソッドにはパラメータが無いため、my_methodとanother_meshodはどちらもpriavteとなる。

publicアクセスレベルは説明するまでもなく、メソッドのアクセスや可視性に制限が無いことを意味している。
privateアクセスレベルは、同じクラスかそのサブクラス内からしかメソッドにアクセスできない。
つまり、privateではselfを(暗黙的または明示的に)レシーバとする「関数形式」でしかメソッドを呼び出せない。

protectedアクセスレベルは、メソッドを同じクラス内からしか呼び出せない事を意味している。
privateアクセスレベルとの違いは、同じクラスの別のインスタンスなど、レシーバがself以外でも呼び出せる点である。

クラスではメソッドのデフォルトの可視性はpublicに定義される。
ただし、インスタンス初期化メソッドであるinitializeは例外となり、newメソッドからしか呼び出されない為、privateとなる。
トップレベルで定義されるメソッドもデフォルトでpublicとなる。
privateに定義した場合は関数形式でしか呼び出すことができない。

Rubyのクラスはそれ自体がオブジェクトであり、Classメタクラスインスタンスになっている。
Rubyのクラスは常に具体クラスで、抽象クラスは無い。
ただし、本当に必要であれば、理論上はRubyで抽象クラスを実装することも可能である。

階層のルートにあるのがObjectクラスである。
Objectクラスは組み込みのKernelモジュールで定義されている全てのメソッドを提供する。

class MyClass < OtherClass
  # ...
end

組み込みメソッドを使うだけでなく、独自のメソッドを定義したり、既存のメソッドを再定義してオーバーライドする事も勿論可能。
既存のメソッドと同じ名前のメソッドを定義した場合、元のメソッドはオーバーライドされる。
メソッドがオーバーライドした「親」メソッドを呼び出す必要がある場合は、キーワード「super」を利用する。

演算子のオーバーロードは、厳密にはオブジェクト指向プログラミングの機能では無いが、C++プログラマなどにとっては非常に身近な機能である。
Rubyの多くの演算子は単なるメソッドであるため、当然ながらこれらの演算子はオーバーライドすることができ、ユーザー定義クラス用に定義することも可能。
既存のクラスの演算子の意味をオーバーライドする事はめったにないと考えられるが、新しいクラスの演算子を定義することは頻繁にある。

メソッドには別名をつける事が出来る。
クラス定義の中で使う構文は次のとおり。

alias newname oldname

パラメータの数は元の名前のメソッドと同じで、呼び出し方も同じになる。


contentsへ