クラスインスタンス変数
2007 年 1 月 9 日
オブジェクトについて学ぶと、通常、オブジェクトがインスタンスとクラスの 2 種類のデータをキャプチャできることを学びます。インスタンス変数が最も一般的なケースであり、そのデータはオブジェクトのインスタンスごとに異なります。静的変数と呼ばれることが多いクラス変数は、クラスのすべてのインスタンスで共有されます。すべてのインスタンスが同じ値を指し、変更はすべてに見えます。クラス変数はインスタンス変数よりもはるかに一般的ではなく、特に変更可能なクラス変数の場合は特にそうです。
クラス変数の特定のしわは、それらが継承とどのようにやり取りするかです。そのインスタンスを格納するために使用されるクラス変数を検討してください。(ruby に詳しくない場合は、リーディングガイド を参照してください)。
#ruby class Employee @@instances = [] def self.instances return @@instances end def store @@instances << self end def initialize name @name = name end end Employee.new('Martin').store Employee.new('Roy').store Employee.new('Erik').store puts Employee.instances.size
驚きはなく、従業員は 3 人います。しかし、今度はこれを実行してみましょう。
#ruby class Employee @@instances = [] def self.instances @@instances end def store @@instances << self end def initialize name @name = name end end class Programmer < Employee; end class Overhead < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size puts Programmer.instances.size
ここの出力は 3 と 3 ですが、おそらく 2 と 1 を優先するでしょう。理由は、クラス変数は、すべてのサブクラスを含むクラスのすべてのインスタンスで共有されるからです。クラスは 2 つありますが、変数は 1 つしかありません。
階層全体でこの変数はまさに私たちが望むものである場合もありますが、この場合のように、クラスごとに異なる変数を使用することを好む場合もあります。クラスインスタンス変数のコンセプトに出会ったのは、Smalltalk のより新しいバージョンで「クラスインスタンス変数」という名称でした。クラス変数と同じ方法でクラスインスタンス変数を参照できますが、クラスごとに異なる値を取得できます。
クラスインスタンス変数のサポートは OO 言語では一般的ではありませんが、難しくはありません。明らかな方法は、クラス名でキー付けされたディクショナリを使用することです。
#ruby class Employee @@instances = {} def self.instances @@instances[self] end def store @@instances[self.class] ||= [] @@instances[self.class] << self end def initialize name @name = name end end class Overhead < Employee; end class Programmer < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size puts Programmer.instances.size
このテクニックはどの OO 言語でも使用できます。ただし、Ruby には実際にはクラスインスタンス変数が含まれています。
#ruby class Employee class << self; attr_accessor :instances; end def store self.class.instances ||= [] self.class.instances << self end def initialize name @name = name end end class Overhead < Employee; end class Programmer < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size puts Programmer.instances.size
クラスインスタンス変数の定義は、フラグメント class << self; attr_accessor :instances; end
です。私が本当に踏み込みたいとは思いませんが、これはその子孫によって継承されるクラスの従業員に対してインスタンス変数 (およびゲッターとセッター) を定義します。クラス変数とは異なり、これらのクラスインスタンス変数はクラスオブジェクトごとに異なる値になります。
クラスのインスタンス変数は比較的まれですが、必要なときには便利です。