0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

読書ログ『メタプログラミング Ruby 第2版』5章

Posted at

5章: 木曜日: クラス定義

はじめに

  • Rubyのclassキーワードは、オブジェクトの動作規定だけでなく、実際にコードを実行している
  • この章では、クラスマクロ(クラスを修正する方法) / アラウンドエイリアス(メソッドをコードでラップする方法) / 特異クラス(シングルトンクラス) を説明する

クラス定義とは

  • クラス定義にはあらゆるコードを置くことができる
    • メソッドを定義するだけの場所ではない
class MyClass
	puts 'Hello'
end
  • メソッドやブロックと同じように、クラス定義も最後の命令文の値を戻す
  • クラス(やモジュール)定義の中では、クラスがカレントオブジェクトselfになる
    • クラスとモジュールも単なるオブジェクトにすぎない。
result = class Myclass
	self
end

result #=> MyClass

カレントクラス

  • Rubyのプログラムは、常にカレントオブジェクトselfと カレントクラスを持つ
    • メソッドを定義すると、カレントクラスのインスタンスメソッドに
  • プログラムのトップレベルでは、カレントクラスは、mainのクラスのObjectになる。
  • メソッドの中では、カレントオブジェクトのクラスが、カレントクラスに。

カレントオブジェクト

  • self
  • インスタンスメソッドが呼び出されるレシーバのこと

Module#class_eval

  • クラス名がわからない時にクラスをオープンするために用いる
    • クラスのコンテキストでブロックを評価するもの
def add_method_to(a_class)
	a_class.class_eval do
		def m; 'Hello!'; end
	end
end

add_method_to Strign
"abc".m #=> "Hello!"
"abc".m.class #=> String
  • class_evalは、selfとカレントクラスを変更する
  • カレントクラスを変更することで、クラスを再オープンできる
  • classが定数を必要とするのに対し、class_evalはクラスを参照する変数ならなんでも使える
  • class_evalは、フラットスコープを持つので、class_evalブロックのスコープの外側の変数も参照できる

instance_evalとclass_evalの使い分け

  • クラス以外のオブジェクトをオープンしたいなら、instance_eval
  • クラス定義をオープンして、defを使ってメソッドを定義したいなら、class_eval

カレントクラスのまとめ

  • Rubyのインタプリタは、常にカレントクラスの参照を追跡
    • defで定義された全てのメソッドは、カレントクラスのインスタンスメソッドに。
  • クラス定義の中では、カレントオブジェクトselfとカレントクラス(定義クラス)は同じ
  • クラスへの参照を持っていれば、クラスはclass_evalでオープンできる

クラスインスタンス変数

  • クラスのインスタンス変数と、クラスのオブジェクトのインスタンス変数は別物

  • クラスのスコープで定義した変数のことを「クラスインスタンス変数」

  • メソッドのスコープで定義した変数を「インスタンス変数」

    • 参考 この記事がすごく参考になりました。
  • アクセスできるのはクラスだけであり、クラスのインスタンスやサブクラスからはアクセスできない

class MyClass
	@my_var = 1
	def self.read; @my_var; end
	def write; @my_var = 2; end
	def read; @my_var; end
end

obj = MyClass.new
# インスタンスメソッドでアクセスできない。
obj.read #=> nil
obj.write
# インスタンスメソッドwriteで定義したものを呼び出す
obj.read #=> 2
# クラスメソッドで呼び出せる!
MyClass.read #=> 1 

クラス変数

  • @@プレフィックスをつけたもの

    class C
    	@@v = 1
    end
    
  • クラス変数は、サブクラスや通常のインスタンスメソッドからもアクセスできる

  • クラス変数は、クラスではなく クラス階層に属している

    • ハマることがあるので、Rubyistはクラス変数を使わず、クラスインスタンス変数を推奨

特異メソッド

  • 単一のオブジェクトに特化したメソッド
    • 特定のオブジェクトにメソッドを追加できる
  • Object#define_singleton_methodで定義できる
str = "just a regular string"

def str.title?
	self.upcase == self
end

self.title? #=> false
str.methods.grep(/title?/)	#=> [:title?]
str.singleton_methods				#=> [:title?]

Rubyにおける型

  • 「型」はオブジェクトが反応するメソッドの集合に過ぎない
    • Rubyなどの動的言語では、オブジェクトの「型」はそのクラスとは厳密に結びついていない
  • ダックタイピング
    • 「型」はオブジェクトが反応するメソッドの集合に過ぎないという、流動的な型のこと。

クラスメソッドの真実

  • クラスは単なるオブジェクトであり、クラス名は単なる定数
  • クラスメソッドは、クラスの特異メソッド

クラスマクロ

  • クラス定義の中で使えるクラスメソッド
    • ex) attr_accessor

特異クラス

  • オブジェクトの通常クラスとは別に持つ、特別なクラスのこと
    • メタクラスやシングルトンクラスとも
  • 特異クラスは、オブジェクトの特異メソッドが住んでいる場所
    • Object#singleton_class か、 class << 構文 によってみることができる
  • 特異クラスは、インスタンスを一つしか持てない
    • だから、 シングルトンクラス
  • オブジェクトが特異メソッドを持っていると、Rubyは通常のクラスではなく、特異クラスのメソッドから探索を始める。

Rubyのオブジェクトモデルの7つのルール

  • オブジェクトは1種類しかない
    • それが、通常のオブジェクトかモジュールに
  • モジュールは1種類しかない
    • それが通常のモジュール、クラス、特異クラスのいずれかに
  • メソッドは1種類しかない
    • メソッドはモジュール(大半はクラス)に住んでいる
  • 全てのオブジェクト(クラス含む)は、「本物のクラス」を持つ
    • それが通常のクラスか特異クラス
  • 全てのクラス(BasicObjectを除く)は、一つの祖先を持つ
    • あらゆるクラスがBasicObjectに向かって、1本の継承チェーンをもつ。
  • オブジェクトの特異クラスのスーパークラスは、オブジェクトのクラス
    • クラスの特異クラスのスーパークラスは、クラスのスーパークラスの特異クラス
  • メソッドを呼び出すときは、Rubyはレシーバの本物のクラスに向かって、「右へ」煤sみ、継承チェーンを「上へ」進む。

クラスメソッドの構文

  • self.メソッドを使う場合
class MyClass
	def self.another_class_method; end
end
  • << self を使う場合
class MyClass
	class << self
		def yet_another_class_method; end
	end
end

メソッドラッパー

アラウンドエイリアス

  • Module#alias_methodを使って、Rubyのメソッドにエイリアスをつける
    • alias_methodを使うときは、メソッドの新しい名前を先に、元の名前を後に
class myClass
	def my_method; 'my_method()'; end
	alias_method :m, :my_method
end

obj = MyClass.new
obj.my_method	#=> "my_method()"
obj.m					#=> "my_method()"

メソッドの再定義

  • 新しいメソッドを定義して、元の名前をつけること
    • 元のメソッドを変更することではない

アラウンドエイリアスをまとめると

  • メソッドにエイリアスをつける
  • メソッドを再定義する
  • 新しいメソッドから古いメソッドを呼び出す。

感想

む、、、むずい。なんとか食いついたという感覚:sweat:

時間を置いてもう一度読み直す。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?