クラスインスタンス変数

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 です。私が本当に踏み込みたいとは思いませんが、これはその子孫によって継承されるクラスの従業員に対してインスタンス変数 (およびゲッターとセッター) を定義します。クラス変数とは異なり、これらのクラスインスタンス変数はクラスオブジェクトごとに異なる値になります。

クラスのインスタンス変数は比較的まれですが、必要なときには便利です。