Rails: クラスレベルの3つのアクセサの使い方を比較する(翻訳)
attr_accessor
は、インスタンス変数のgetterとsetterを提供する非常に有用なマクロです。これと同じ効果をクラス変数でも得られれば、クラス変数でクラスを設定できて便利な場合もあります。
そのための方法として、少なくともattr_accessor
、cattr_accessor
、class_attribute
の3つのアプローチがあります。継承を使わない場合はどれも同じに使えますが、継承がからむ場合の動作はかなり異なります。
🔗 1. attr_accessor
: 値を継承したくない場合に使う
この場合は通常のattr_accessor
を使いますが、ここではクラスレベルで使います。attr_accessor
を使うと、各子クラスに変数が定義されますが、親クラスに設定された値は継承されません。各子クラスの属性の初期値はnil
に設定されます。
class Parent
class << self
attr_accessor :foo
end
end
class Child < Parent
end
Parent.foo = 100
Child.foo #=> nil (値は継承されない)
Child.foo = 200
Child.foo #=> 200
Parent.foo #=> 100 (子クラスで値を変更しても親クラスの値には影響しない)
🔗 2. cattr_accessor
: 値をすべてのクラスで共有したい場合に使う
この場合はcattr_accessor
で親クラスの値を子クラスに継承します。この場合、cattr_accessor
宣言をclass << self
ブロックの内側に置く必要はありません。ここで重要なのは、子クラスでクラス変数の値を再定義すると親クラスと子クラスの両方で値が変更されるという点です。
class Parent
cattr_accessor :foo
end
class Child < Parent
end
class Grandchild < Child
end
Parent.foo = 100
Child.foo #=> 100
Child.foo = 200
Child.foo #=> 200
Parent.foo #=> 200 (値は継承ツリーの上位に反映される)
Grandchild.foo #=> 200 (値は継承ツリーの下位に反映される)
🔗 3. class_attribute
: 継承した値を親クラスに影響を与えずに上書き可能にしたい場合に使う
私はこのシナリオが最も有用だと思います。class_attribute
を使うと親クラスの変数値が継承され、継承した値は親クラスに影響することなく子クラスで安全に再定義できます。
class Parent
class_attribute :foo
end
class Child < Parent
end
class Grandchild < Child
end
Parent.foo = 100
Child.foo #=> 100 (値は継承される)
Child.foo = 200
Child.foo #=> 200
Parent.foo #=> 100 (値を再定義しても親クラスには影響しない)
Grandchild.foo #=> 200 (再定義した値はその子クラスに継承される)
概要
原著者の許諾を得て翻訳・公開いたします。
参考: Rails API
cattr_accessor
--Module
参考: Rails API
class_attribute
--Class