OneDay

40歳からプログラマとして生活しています。

Rubyクラス変数 @@var

あけましておめでとうございます。

新たな年になりましたので、葬送Webサービスを展開するNoGraveの管理人として本年の抱負を述べます。

  1. Webサービスを新規で2つやります。
  2. 積極的に技術の勉強会に参加します。
  3. Ruby/Ruby on Railsに加え、新たな言語習得します。


新たなWebサービスについて構想は出来ています。
https://nograve.netの IT x 葬送というコンセプトを中心におき、葬送を考える家族において、家族の団結を強めるようなWebサービス作ります。もう一つは、IT x 介護で一つ作ります。ここまで6月までにドメインとって、掛かる費用は出し惜しみせず、一つはHerokuでやってみようか思います。
2つ目の勉強会は昨年11月から参加したのですが、上級者との会話は刺激になりますし、方向性の確認になります。生活する長野での勉強会は少ないので週末を利用し、東京の勉強会に参加する予定です。
最後は新たな言語習得。複数の言語を知っていたほうがやれることは増えるし、プログラミングの造詣が深くなる。Rubyを中心に置きつつも、違う言語習得を行う。候補はSwiftか、Pythonかな。
今年は、頭を使って、今までの勉強の成果を発揮したいと思います。よろしくお願いします。


さて、Rubyのクラス変数。Rubyのしくみ -Ruby Under a Microscope-という書籍で、ようやく理解できました。(出来たと思っています。)復習を兼ねてクラス変数のコード例を記載します。


たとえば、チームでの成果をクラス変数に集計できる。

class Lupin3
	@@treasure = 0

	def job
		@@treasure += 1
	end

	def self.treasure
		@@treasure
	end
end

goemon = Lupin3.new
fujiko = Lupin3.new

goemon.job
fujiko.job

p Lupin3.treasure #2が返る

このクラスから生成されるオブジェクトはドロボウで、ドロボウは仕事をしたら財宝が増えるという簡単なコード。@@treasureは各オブジェクトが仕事をした集計が反映。

このクラス変数、スーパークラスでも値を引き継ぐ。@@treasureはLupin一族の財宝の集計として使える。

class Lupin1
	@@treasure =0

	def self.job
		@@treasure += 1
	end

	def self.treasure
		@@treasure
	end

end

class Lupin2 < Lupin1; end

class Lupin3 < Lupin2
	def self.job
		@@treasure += 2
	end

	def team_job
		@@treasure += 3
	end
end

Lupin1.job
Lupin2.job
Lupin3.job

p Lupin1.treasure #4が返る
p Lupin2.treasure #4が返る
p Lupin3.treasure #4が返る

gigen = Lupin3.new
goemon = Lupin3.new
fujiko  = Lupin3.new

gigen.team_job
goemon.team_job
fujiko.team_job

p Lupin1.treasure #13が返る
p Lupin2.treasure #13が返る
p Lupin3.treasure #13が返る

Lupin1、Lupin2、Lupin3のクラスそれぞれに仕事をさせるとLupin1とLupin2は+1され、Lupin3は+2される。よって、それぞれのクラスが一度ずつjobメソッドを実施すると@@treasureの値は4になる。Lupin3はteam_jobというメソッドがあり、オブジェクトに仕事をさせると+3加算される。gigenとgoemonとfujikoにteam_jobさせたから、3 x 3加算される。@@treasureは4+9で13となる。treasureメソッドを用いてどのクラスからでも同じ値を得る。そうサブクラスを含めて同じ値を共有するのだ。うーん、どこかで使えそう。

しかし、そう単純ではない。Lupinの例では使用したメソッドがすべて加算だったから、単純に見えただけで、これが文字列の置き換えだったらどうなるか示す。

class Klass
	@@class_val = "class_val in Klass"

	def class_val
		@@class_val
	end
end

class ChildKlass < Klass
	@@class_val = "class_val in ChildKlass"
end

p Klass.new.class_val #"class_val in ChildKlass"が返る

class GrandChildKlass < ChildKlass
	@@class_val = "class_val in GrandChildKlass"
end

p Klass.new.class_val #"class_val in GrandChildKlass"が返る

class ChildKlass < Klass
	@@class_val = "class_val AGAIN in ChildKlass"
end

p Klass.new.class_val #"class_val AGAIN in ChildKlass"が返る

Klass クラスはクラス変数@@class_valを持っており、その値を返すclass_valメソッドを持つ。Klassを継承したChildKlassで@@class_valを定義するとChildKlassで定義したものがclass_valメソッドで返る。GrandChildKlassでも同じ挙動。継承で一番底辺にいるクラス変数を見るのか...? 違う。再度ChildKlassで@@class_valを再定義すると、その再定義した文字列が返る。つまり、クラス変数は一番新しいものに上書きされる。

今読んでいるRubyのしくみ -Ruby Under a Microscope-にはこの動きをこう書いている。

Rubyはクラスをまたいでそのサブクラスすべてでクラス変数を共有する
Rubyは対象クラスとそのスーパークラスのいずれかにクラス変数が既に存在していないかをチェックする
Rubyは最も上位のスーパークラスで見つかったクラス変数のコピーを使用する

Rubyのしくみ -Ruby Under a Microscope-はちょっと難しいが、Rubyのうごきを知ることができて面白い。