Rubyの@変数
Rubyには2つの@変数があり、先頭が@のインスタンス変数
と先頭が@@変数のクラス変数
に分かれる。
インスタンス変数はそれぞれのオブジェクトに属しています。
あるオブジェクトに値がセットされたインスタンス変数は、他のオブジェクトのインスタンス変数には基本的には影響を与えません。
一方で、クラス変数は、オブジェクトごとではなく、クラスごとに与えられます。
クラスから作られたインスタンスは、すべてのインスタンスで参照することができます。
このため、オブジェクトでクラス変数を変更して、意図せずに他のオブジェクトのインスタンス変数を変更してしまうことがあります。
これを、例を示して考えてみます。
シングルトンパターンを実装して@変数を考える
まず、クラス変数が使用される場面として、シングルトンパターンがあります。
シングルトンパターンは、特定のクラスのインスタンスを一つ作り、そのインスタンスをすべてのコードがアクセスできるようにしたい場合に実装されます。
class Singleton
private_class_method(:new, :dup, :clone)
def self.instance
@@instance ||= new
end
end
# 書籍:Effective Rubyより抜粋
Singleton
クラスの中にself.instance
メソッドが定義されています。
これは、インスタンスの存在を確認し、存在しなければ、newメソッドを実行するように定義されています。
また、private_class_method
はpublicなクラスメソッドをprivateなクラスメソッドとして定義できます。
:new, :dup, :clone
をprivateにしているのは、親クラスであるObjectクラスに定義されているメソッドをprivate化して、インスタンスを作る役割をinstanceメソッドに一任しているのがわかります。
では、Singletonクラスを継承するConfigrationクラス
とDatabaseクラス
を定義してみます。
class Configuration < Singleton
end
class Database < Singleton
end
p Configuration.instance
# => <Configuration:0x0000563c0e4d64a8>
p Database.instance
# => <Configuration:0x0000563c0e4d64a8>
実際に実装した結果は、どちらもConfigurationクラスから生成されたクラス変数が出力されています。1つのオブジェクトが他のオブジェクトに影響を与えてしまっているのがわかります。
インスタンス変数を使う
考え方としては、すべてのクラスで一意なオブジェクトとして扱われる必要があります。
冒頭で説明した通り、この場合はインスタンス変数を適用することで、解決することができます。
class Singleton
private_class_method(:new, :dup, :clone)
def self.instance
@instance ||= new
end
end
# 書籍:Effective Rubyより抜粋
クラス変数とインスタンス変数の使用場面には十分注意しましょう!