2013-12-16

Rubyのクラス変数について詰まったのでヘルプ

基本的に、仕事でクラス変数を扱うことはほぼ無いです。

今回は遊びのコードを書いていて、悩んだことをまとめました。

Rubyのクラス変数の扱いが難しい...。

これ、結構Ruby書いている自分でも初めて遭遇して悩む問題だった。

まず、次のような動くコードを書いてみた。

class Base; end

class Node < Base
  @@val = 'Node'
end

class Branch < Base
  @@val = 'Branch'
end

p Node.class_variable_get('@@val') #=> 'Node'
p Branch.class_variable_get('@@val') #=> 'Branch'

ふむ、それぞれの派生クラスでクラス変数が使えますね。

では、続いて基底クラスにもクラス変数を与えてみる。

class Base
  @@val = nil
end

class Node < Base
  @@val = 'Node'
end

class Branch < Base
  @@val = 'Branch'
end

p Node.class_variable_get('@@val') #=> 'Branch'
p Branch.class_variable_get('@@val') #=> 'Branch'

Ooops!!! 派性クラス内のクラス変数が、基底クラス変数を参照しよる!!

解決編

やりたいことは、派生クラスの子クラスに共通の変数を持たせるということ。

うーん、意外と難しい!

どうもクラス変数という感じではなくなるけれど、こうするしかないのだろうか...。

メソッドにしてみたら、とっても気持ちが悪い。

class Base
  class << self
    private

    def val
      'Base'
    end
  end
end

class Node < Base
  class << self
    private

    def val
      'Node'
    end
  end
end

class Branch < Base
  class << self
    private

    def val
      'Branch'
    end
  end
end

p Node.send(:val) #=> 'Node'
p Branch.send(:val) #=> 'Branch'

これだと、一応コードとして動く。 ただ、これは無いだろ感あるよね。

しかし、クラスインスタンス変数だと継承されないし、こうするしかないのかなぁ。

だれか正しい方法があれば、教えてください! >_<

追記

思いついたんだけど、継承するときにクラスインスタンス変数を渡せばいいのかな?

どうでしょう。

class Base
  @val = 'Base'

  def self.inherited(klass)
    klass.instance_variable_set('@val', @val)
  end
end

class Node < Base; end
class Branch < Base; end

p Node.instance_variable_get('@val') #=> 'Base'
p Branch.instance_variable_get('@val') #=> 'Base'