Ruby オブジェクト指向

Rubyは純粋なオブジェクト指向言語で、Rubyにおけるすべてのものはオブジェクトの形で現れます。Rubyのすべての値はオブジェクトであり、最も原始的なものでも、文字列、数値、さらには true や false もオブジェクトです。クラス自体もまたオブジェクトであり、Classクラスのインスタンスです。この章では、Rubyのオブジェクト指向に関連するすべての主要な機能について説明します。

クラスはオブジェクトの形式を指定するために使用され、データ表現とメソッドを組み合わせて、データを整然としたパッケージにまとめます。クラス内のデータとメソッドはクラスのメンバーと呼ばれます。

Ruby クラス定義

クラスを定義するとき、実際にはデータ型の設計図(ブループリント)を定義しています。これは実際にはデータを一切定義するものではなく、クラス名が何を意味するか、つまりクラスのオブジェクトが何で構成され、そのオブジェクトに対してどのような操作を実行できるかを定義します。

クラス定義はキーワードclassclass で始まり、その後にクラス名、最後にendend で区切って、そのクラス定義の終了を示します。例えば、キーワード class を使用して Box クラスを定義する場合は、次のようになります。

class Box code end

慣例により、名前は大文字で始める必要があります。複数の単語が含まれる場合、各単語の先頭は大文字にしますが、その間に区切り文字はありません(例:CamelCase)。

Ruby オブジェクトの定義

クラスはオブジェクトの設計図を提供するため、基本的にオブジェクトはクラスに基づいて作成されます。私たちはnewnew キーワードを使用してクラスのオブジェクトを宣言します。次の文は、クラス Box の2つのオブジェクトを宣言しています。

box1 = Box.new box2 = Box.new

initialize メソッド

initializeこのメソッドは標準的なRubyのクラスメソッドであり、クラスのコンストラクタです。他のオブジェクト指向プログラミング言語におけるconstructorコンストラクタと同様に機能します。オブジェクトの作成と同時にいくつかのクラス変数を初期化したい場合、initialize メソッドが役立ちます。このメソッドは一連のパラメータを受け取ります。他のRubyメソッドと同様に、このメソッドを使用するときは、その前にdefキーワードを置く必要があります。次のようになります。

class Box def initialize(w,h) @width, @height = w, h end end

インスタンス変数

インスタンス変数はクラスの属性であり、クラスを使用してオブジェクトを作成すると、それらはオブジェクトの属性になります。各オブジェクトの属性は個別に割り当てられ、他のオブジェクトと値は共有されません。クラスの内部では、@ 演算子を使用してこれらの属性にアクセスします。クラスの外部では、いわゆるアクセサメソッドのパブリックメソッドを使用してアクセスします。次に、上で定義したクラスBoxを例として、@width と @height をクラス Box のインスタンス変数とします。

class Box def initialize(w,h) #インスタンス変数への代入 @width, @height = w, h end end

アクセサ(getter) & セッター(setter)メソッド

クラスの外部でクラス内に定義された変数を読み取るために、アクセサ(getter)メソッドを定義してアクセスできます。次の例は、アクセサメソッドの使い方を示しています。

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタ def initialize(w,h) @width, @height = w, h end #アクセサメソッド def printWidth @width end def printHeight @height end end #オブジェクトを作成し、ボックスの高さと幅を初期化 box = Box.new(10, 20) #アクセサメソッドを使用 x = box.printWidth() y = box.printHeight() puts "ボックスの幅 : #{x}" puts "ボックスの高さ : #{y}"

試してみる »

上記のコードを実行すると、次の結果が生成されます。

盒子宽度 : 10
盒子高度 : 20

変数値にアクセスするためのアクセサメソッドと同様に、Rubyにはクラスの外部からクラス内に定義された変数にパラメータを渡す方法、いわゆるセッターメソッドが用意されています。定義は次のとおりです。

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #アクセサメソッド def getWidth @width end def getHeight @height end #セッターメソッド def setWidth=(value) @width = value end def setHeight=(value) @height = value end end #オブジェクトを作成 box = Box.new(10, 20) #セッターメソッドを使用 box.setWidth = 30 box.setHeight = 50 #アクセサメソッドを使用 x = box.getWidth() y = box.getHeight() puts "ボックスの幅 : #{x}" puts "ボックスの高さ : #{y}"

試してみる »

上記のコードを実行すると、次の結果が生成されます。

盒子宽度 : 30
盒子高度 : 50

この2つのメソッドは非常に頻繁に使われるため、Rubyではattr_accessor :variable_name、attr_reader :variable_name、attr_writer :variable_name3つの属性宣言メソッドが定義されています。そのうち:accessor=reader+writer。

同時に注意:変数名の前には必ず:@ を付け、変数名の間は,, で区切ります。

インスタンスメソッド

インスタンスメソッドの定義は他のメソッドの定義と同様に、すべてdefdef キーワードを使用しますが、クラスインスタンスを介してのみ使用できます。次の例に示すとおりです。それらの機能はインスタンス変数へのアクセスに限定されず、ニーズに応じて他の多くのタスクを実行することもできます。

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #インスタンスメソッド def getArea @width * @height end end #オブジェクトを作成 box = Box.new(10, 20) #インスタンスメソッドを呼び出し a = box.getArea() puts "Area of the box is : #{a}"

試してみる »

上記のコードを実行すると、次の結果が生成されます。

Area of the box is : 200

クラスメソッド & クラス変数

クラス変数は、クラスのすべてのインスタンス間で共有される変数です。言い換えると、クラス変数のインスタンスは、すべてのオブジェクトインスタンスからアクセスできます。クラス変数は2つの @ 文字(@@)をプレフィックスとして持ち、クラス定義内で初期化する必要があります。次の例に示すとおりです。

クラスメソッドdef を使用してdef self.methodname()定義され、クラスメソッドは end 区切り文字で終わります。クラスメソッドはクラス名を伴うclassname.methodname形式で呼び出すことができます。次の例に示すとおりです。

例

#!/usr/bin/ruby -w class Box #クラス変数を初期化 @@count = 0 def initialize(w,h) #インスタンス変数に値を代入 @width, @height = w, h @@count += 1 end def self.printCount() puts "Box count is : #@@count" end end #2つのオブジェクトを作成 box1 = Box.new(10, 20) box2 = Box.new(30, 100) #クラスメソッドを呼び出してボックスの個数を出力 Box.printCount()

試してみる »

上記のコードを実行すると、次の結果が生成されます。

Box count is : 2

to_s メソッド

定義したすべてのクラスにはto_sオブジェクトの文字列表現を返すインスタンスメソッドがあります。次は、width と height に基づいて Box オブジェクトを表す簡単な例です。

例

#!/usr/bin/ruby -w class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #to_s メソッドを定義 def to_s "(w:#@width,h:#@height)" #オブジェクトの文字列形式 end end #オブジェクトを作成 box = Box.new(10, 20) #to_s メソッドを自動的に呼び出し puts "String representation of box is : #{box}"

試してみる »

上記のコードを実行すると、次の結果が生成されます。

String representation of box is : (w:10,h:20)

アクセス制御

Rubyは、インスタンスメソッド保護の3つのレベルを提供します。それぞれpublic、private または protectedです。Rubyはインスタンス変数とクラス変数にはアクセス制御を適用しません。

  • Public メソッド:Public メソッドは任意のオブジェクトから呼び出せます。デフォルトでは、メソッドはすべて public ですが、initialize メソッドだけは常に private です。
  • Private メソッド:Private メソッドはクラスの外部からアクセスまたは参照できません。クラスメソッドのみがプライベートメンバーにアクセスできます。
  • Protected メソッド:Protected メソッドは、クラスとそのサブクラスのオブジェクトからのみ呼び出せます。アクセスもクラスとそのサブクラスの内部でのみ行えます。

次は、これら3つの修飾子の構文を示す簡単な例です。

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #インスタンスメソッドはデフォルトで public def getArea getWidth() * getHeight end #private なアクセサメソッドを定義 def getWidth @width end def getHeight @height end # make them private private :getWidth, :getHeight #面積を出力するためのインスタンスメソッド def printArea @area = getWidth() * getHeight puts "Big box area is : #@area" end #インスタンスメソッドを protected にする protected :printArea end #オブジェクトを作成 box = Box.new(10, 20) #インスタンスメソッドを呼び出し a = box.getArea() puts "Area of the box is : #{a}" #protected なインスタンスメソッドの呼び出しを試みる box.printArea()

試してみる »

上記のコードを実行すると、次の結果が生成されます。ここでは、最初のメソッド呼び出しは成功しますが、2番目のメソッドは問題を引き起こします。

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height=20, @width=10> (NoMethodError)

クラスの継承

継承は、オブジェクト指向プログラミングにおける最も重要な概念の一つです。継承により、別のクラスに基づいてクラスを定義できるため、アプリケーションの作成と保守が容易になります。

継承はコードの再利用と高速実行に役立ちます。残念ながら、Ruby は多重継承をサポートしていませんが、Ruby はサポートしていますmixins。mixin は多重継承の特定の実装のようなもので、多重継承ではインターフェース部分のみが継承可能です。

クラスを作成するとき、プログラマーは新しいクラスが既存のクラスのメンバーを継承するように直接指定できます。これにより、新しいデータメンバーとメンバー関数を最初から書く必要がありません。この既存のクラスは基本クラスまたは親クラス、新しいクラスは派生クラスまたは子クラス。

Ruby もサブクラス化の概念を提供します。サブクラス化とは継承のことです。以下の例でこの概念を説明します。クラスを拡張する構文は非常に簡単です。< 文字と親クラスの名前をクラス文に追加するだけです。たとえば、以下はクラスを定義していますBigBoxであるBoxのサブクラス:

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #インスタンスメソッド def getArea @width * @height end end #サブクラスを定義 class BigBox < Box #新しいインスタンスメソッドを追加 def printArea @area = @width * @height puts "Big box area is : #@area" end end #オブジェクトを作成 box = BigBox.new(10, 20) #面積を出力 box.printArea()

試してみる »

上記のコードを実行すると、次の結果が生成されます:

Big box area is : 200

メソッドのオーバーロード

派生クラスに新しい機能を追加することはできますが、親クラスで既に定義されているメソッドの動作を変更したい場合もあります。この場合、メソッド名を変えずにメソッドの機能をオーバーライド(再定義)するだけで済みます。次の例のようになります。

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #インスタンスメソッド def getArea @width * @height end end #サブクラスを定義 class BigBox < Box #既存の getArea メソッドを変更 def getArea @area = @width * @height puts "Big box area is : #@area" end end #オブジェクトを作成 box = BigBox.new(10, 20) #オーバーライドしたメソッドを使用して面積を出力 box.getArea()

試してみる »

上記の例を実行したときの出力結果は次のとおりです:

Big box area is : 200

演算子のオーバーロード

私たちは、+ 演算子を使用して2つの Box オブジェクトのベクトル加算を実行し、* 演算子を使用して Box の width と height を乗算し、単項演算子 - を使用して Box の width と height の符号を反転したいと考えています。以下は、数学演算子の定義を備えた Box クラスのバージョンです:

class Box def initialize(w,h) #width と height を初期化 @width,@height = w, h end def +(other) #+ を定義してベクトル加算を実行 Box.new(@width + other.width, @height + other.height) end def -@ #単項演算子 - を定義して width と height の符号を反転 Box.new(-@width, -@height) end def *(scalar) #スカラー乗算を実行 Box.new(@width*scalar, @height*scalar) end end

オブジェクトの凍結

時には、オブジェクトが変更されるのを防ぎたいことがあります。Object の freeze メソッドはこれを実現し、オブジェクトを事実上定数にします。任意のオブジェクトは、freeze を呼び出すことでObject.freeze凍結できます。凍結されたオブジェクトは変更できません。つまり、そのインスタンス変数を変更することはできません。

あなたは、Object.frozen?メソッドを使用して、指定されたオブジェクトが既に凍結されているかどうかを確認できます。オブジェクトが凍結されている場合、そのメソッドは true を返し、そうでなければ false の値を返します。以下の例でこの概念を説明します。

例

#!/usr/bin/ruby -w #クラスを定義 class Box #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #アクセサメソッド def getWidth @width end def getHeight @height end #セッターメソッド def setWidth=(value) @width = value end def setHeight=(value) @height = value end end #オブジェクトを作成 box = Box.new(10, 20) #そのオブジェクトを凍結しましょう box.freeze if( box.frozen? ) puts "Box object is frozen object" else puts "Box object is normal object" end #セッターメソッドを使用してみます box.setWidth = 30 box.setHeight = 50 #アクセサメソッドを使用 x = box.getWidth() y = box.getHeight() puts "Width of the box is : #{x}" puts "Height of the box is : #{y}"

試してみる »

上記のコードを実行すると、次の結果が生成されます:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
        from test.rb:39

クラス定数

クラスの内部で定数を定義できます。これは、直接的な数値または文字列の値を変数に代入することで定義されます。定数の定義に @ や @@ を使う必要はありません。慣例により、定数の名前は大文字を使用します。

定数を定義すると、その値を変更することはできません。クラスの内部では、変数にアクセスするのと同じように定数に直接アクセスできます。ただし、クラスの外部から定数にアクセスしたい場合は、次を使用する必要がありますclassname::constant。以下の例のとおりです。

例

#!/usr/bin/ruby -w #クラスを定義 class Box BOX_COMPANY = "TATA Inc" BOXWEIGHT = 10 #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #インスタンスメソッド def getArea @width * @height end end #オブジェクトを作成 box = Box.new(10, 20) #インスタンスメソッドを呼び出し a = box.getArea() puts "Area of the box is : #{a}" puts Box::BOX_COMPANY puts "Box weight is: #{Box::BOXWEIGHT}"

試してみる »

上記のコードを実行すると、次の結果が生成されます:

Area of the box is : 200
TATA Inc
Box weight is: 10

クラス定数は継承でき、インスタンスメソッドと同様にオーバーライド(再定義)することもできます。

allocate を使用したオブジェクトの作成

オブジェクトコンストラクタを呼び出さないでinitializeオブジェクトを作成したい場合があります。つまり、new メソッドを使用してオブジェクトを作成する場合です。このような状況では、allocate を呼び出して未初期化のオブジェクトを作成できます。次の例のようになります。

例

#!/usr/bin/ruby -w #クラスを定義 class Box attr_accessor :width, :height #コンストラクタメソッド def initialize(w,h) @width, @height = w, h end #インスタンスメソッド def getArea @width * @height end end #new を使用してオブジェクトを作成 box1 = Box.new(10, 20) #allocate を使用して別のオブジェクトを作成 box2 = Box.allocate #box1 を使用してインスタンスメソッドを呼び出し a = box1.getArea() puts "Area of the box is : #{a}" #box2 を使用してインスタンスメソッドを呼び出し a = box2.getArea() puts "Area of the box is : #{a}"

試してみる »

上記のコードを実行すると、次の結果が生成されます:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

クラス情報

Ruby の self と Java の this には類似点がありますが、大きく異なります。Java のメソッドはすべてインスタンスメソッド内で参照されるため、this は通常現在のオブジェクトを指します。一方、Ruby のコードは行ごとに実行されるため、異なるコンテキスト(context)では self の意味が異なります。次の例を見てみましょう。

例

#!/usr/bin/ruby -w class Box #クラス情報を出力 puts "Class of self = #{self.class}" puts "Name of self = #{self.name}" end

試してみる »

上記のコードを実行すると、次の結果が生成されます:

Class of self = Class
Name of self = Box

これは、クラス定義がそのクラスを現在のオブジェクトとして実行できることを意味し、また、メソッド定義の実行中にメタクラスと親クラスのそのメソッドが利用可能であることも意味します。

その他の拡張