やりたいこと
継承可能なクラス属性を定義したい。
class A
self.x = :hoge
end
class B < A
end
class C < B
self.x = :fuga
end
A.x #=> :hoge
B.x #=> :hoge (親の x を継承する。)
C.x #=> :fuga (再定義した場合はその値を使う。)
たったひとつの冴えたやり方
Active Support コア拡張の Class#class_attribute を使う。
require 'active_support/core_ext/class/attribute'
class A
class_attribute :x
self.x = :hoge
end
class B < A
end
class C < B
self.x = :fuga
end
A.x #=> :hoge
B.x #=> :hoge
C.x #=> :fuga
他に考えた方法
クラス変数を使う (失敗)
クラス変数はサブクラスとも共有され、サブクラスで代入できるためこの用途に使えない。
class A
@@x = :hoge
end
class B < A
end
class C < B
@@x = :fuga
end
A.class_variable_get(:@@x) #=> :fuga 😭
B.class_variable_get(:@@x) #=> :fuga 😭
C.class_variable_get(:@@x) #=> :fuga
クラスインスタンス変数を使う (失敗)
クラスインスタンス変数はサブクラスとは共有されず、各クラスで独立している。しかし継承もしない。
class A
@x = :hoge
end
class B < A
end
class C < B
@x = :fuga
end
A.instance_variable_get(:@x) #=> :hoge
B.instance_variable_get(:@x) #=> nil 😭
C.instance_variable_get(:@x) #=> :fuga
クラスインスタンス変数を使い、継承時に親クラスの値を引き継ぐ (成功)
class A
def self.inherited(subclass)
subclass.instance_variable_set(:@x, @x)
end
@x = :hoge
end
class B < A
end
class C < B
@x = :fuga
end
A.instance_variable_get(:@x) #=> :hoge
B.instance_variable_get(:@x) #=> :hoge
C.instance_variable_get(:@x) #=> :fuga