1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

Rubyのオブジェクト指向のアプローチによるプログラミング学習雑学

2022-01-31 09:38:56

開講クラス

StringやArrayのような標準的なライブラリのクラスであっても、既存のクラスを再度オープンして動的に変更することが可能です。このような動作をオープンクラスと呼びます。

モンキーパッチ

あるクラスに不用意に新しい機能を追加して、そのクラスの本来の機能を上書きし、それがコードの他の部分に影響を与える場合、そのようなパッチはモンキーパッチと呼ばれます。

クラスとモジュール

Ruby の class キーワードは、型宣言文というよりもスコープ演算子です。class キーワードの核となるタスクは、クラスのコンテキストに入り込み、その中でメソッドを定義できるようにすることです。

すべてのクラスはモジュールであり、クラスは、クラスの継承構造を整理し、オブジェクトを作成する3つのメソッド(new、allocate、superclass)を備えた拡張モジュールです。

Rubyにおけるクラスとモジュールの概念は、互いに置き換えられるほど近いものであり、両方を維持する理由は、コードを明確にし、コードの意図をより明確にするためである。使い分けの原則です。

  • 自分のコードを他のコードに含める場合は、モジュールを使用する必要があります。
  • コードの一部をインスタンス化または継承させたい場合は、クラスを使用する必要があります。
  • モジュール機構は、他の言語で見られるような名前空間の概念を実装するために使用することができます。

Rubyの::記法

Ruby における定数のパス (スコープ) は、ファイルシステムにおけるディレクトリと同様に :: で分割されてアクセスされ、デフォルトでは変数のパスのルート位置を示すために直接 :: で始まります (例 ::: Y)。

オブジェクトとは

オブジェクトは、インスタンス変数とそのクラスへの参照のセットです。オブジェクトのメソッドは、オブジェクト自体にはなく、オブジェクトのクラスに存在します。

クラスとは

クラスはオブジェクト(Classクラスのインスタンス)とインスタンスメソッドのセットとそのスーパークラスへの参照です。ClassクラスはModuleクラスのサブクラスであり、クラスはモジュールでもあります。

loadメソッドとrequireメソッドの類似点・相違点

loadとrequireはどちらも他人のコードをインポートするために使うことができます。違いは、loadメソッドはコードの読み込みに使われ、現在の名前空間を汚したくない場合は、明示的にload('file.rb',true)で匿名モジュールを作成してもらい、file.rbの定数を引き継ぐ必要があり、requireはライブラリの読み込みに使われる点です。また、loadメソッドは呼び出されるたびに読み込まれたファイルを再度実行しますが、requireは各ライブラリファイルを一度だけ読み込みます。

プリペンド、インクルード、祖先の連鎖

クラスとモジュールは親子関係なので、祖先チェーンはモジュールを含むことができ、prepend と include はそれぞれモジュールをチェーンに追加できますが、違いは include メソッドが呼ばれたとき、モジュールは現在のクラスの真上の祖先チェーンに挿入され、prepend も祖先チェーンに挿入される一方、他の位置はです。

プライベートルール

プライベートメソッドは、明示的にアクセプタを指定して呼び出すことはできません。プライベートメソッドは暗黙の受信者である自分からしか呼び出すことができない(Object#sendは例外)

自己関連

メソッドが呼び出されたとき、アクセプタは self の役割を果たす 明示的にアクセプタを指定しないメソッド呼び出しは self への呼び出しとして扱われる モジュール(またはクラス)が定義されたとき、そのモジュール(またはクラス)は self の役割を演じる。

オブジェクト、クラス、モジュール間の関係

上のModule.classもClassを指しており、上のボックスの中身はすべてClassだが、その親子構成はsuperclassによって確立されており、相違点や類似点があり、それはClass.ancestorsで確認できると解釈できる。

動的なメソッド

メソッドを動的に呼び出す

Ruby では、Object#send メソッドを使用すると、ポイント識別子ではなく、オブジェクトの指定されたインスタンスメソッドを呼び出すことができます。

コード例

class MyClass
  def my_method(my_arg)
    my_arg * 2
  end
end

obj = MyClass.new
obj.my_method(3) #=> 6
obj.send(:my_method, 3) #=> 6



上記のコードは、直接呼び出しても、sendメソッド呼び出しを使用しても、同じ結果になります。sendを使う利点は、メソッドコールをコーディングの中で動的に決定できることです。このテクニックはメタプログラミングではダイナミックディスパッチとして知られています。

もう1つのポイントは、Object#sendでは、オブジェクトのパブリックメソッドだけでなく、プライベートメソッドも呼び出せるということです。オブジェクトのカプセル化された性質を維持し、プライベートなメソッドを外部に公開したくない場合は、Object#public_send メソッドを使用できます。

メソッドを動的に定義する

Ruby では、メソッドの動的な呼び出しの他に、Module#define_method メソッドとコードブロックによって動的なメソッドを定義する方法が用意されています。

サンプルコード

class MyClass
  define_method :my_method do |my_arg|
    my_arg * 3
  do
end

obj = MyClass.new
obj.my_method(2) #=> 6



上記のコードでは、キーワード def を define_method メソッドに置き換えています。define_method メソッドは、定義できる方法がもう少し柔軟で、関数の定義もコード内で派生して行うことができ、実装に柔軟性を持たせていることを除いて、基本的に同じです。

method_missing メソッド

method_missing メソッドは厳密には定義されていませんが (メソッド一覧には現れません)、本質的にはメソッド検索機構であり、呼び出しを横取りして適切なメソッドに妥当な応答を与えるものです。これは、例外処理で例外を投げるのと同じようなもので、一度に1つのレイヤーを処理するものです。

method_missing は、オブジェクトがメソッドを呼び出すときに、対応するクラスのインスタンスメソッドを探し、見つからなければ BasicObject クラスを見つけるまで祖先の連鎖を探すというメカニズムを使っています。もし見つからなければ、最終的にBasicObject#method_missingが呼び出され、NoMethodError例外がスローされます。

似たようなメソッドをたくさん定義する必要があるときは、method_missingメソッドをオーバーライドして、似たようなメソッドに一様に対応し、すでに定義されているメソッドを呼び出すように動作させることができる。

コード例

class Roulette
 def method_missing(name, *args)
  person = name.to_s.capitalize
  super unless %w[Bob Frank Bill Honda Eric].include? person
  number = 0
  3. times do
   number = rand(10) + 1
   puts "#{number}... "
  end
  "#{person} got a #{number}"
 end
end

number_of = Roulette.new
puts number_of.bob
puts number_of.kitty



動的プロキシ

method_missingを通してカプセル化されたオブジェクトへの呼び出しを集め、それらの呼び出しをカプセル化されたオブジェクトに転送するプロセスは、method_missingがダイナミックで、転送がプロキシである、ダイナミックプロキシと呼ばれます。

const_missing メソッド

method_missing と同様に、定数に関する const_missing メソッドも存在します。存在しない定数を参照するとき、Ruby は定数名をシンボルとして const_missing メソッドに渡します。

ホワイトボードクラス(白紙状態)

メソッドがほとんどないクラスを白紙クラスと呼びますが、BasicObjectクラスを継承することですぐに白紙クラスを手に入れることができます。この方法以外にも、メソッドを削除することで、通常のクラスを白紙クラスに変更することも可能です。

deleteメソッド

メソッドを削除するには、2つの方法があります。

  • モジュール#undef_method
  • モジュール#remove_method

この2つの違いは、Module#undef_methodはすべての(継承されたものも含む)メソッドを削除することです。Module#remove_method は継承されたメソッドを維持したまま、受信者自身のメソッドだけを削除します。

Method_missing を使用した動的メソッドの使用原則

できる限りダイナミックメソッドを使用する。method_missing メソッドは、必要な場合 (特にメソッドの数が多い場合) を除いて、控えめに使用しましょう。