2009-08-09 6 views
34

状況:構成ハッシュを持つ変数をそれぞれ保持する複数のクラスがあります。クラスごとに異なるハッシュですが、クラスのすべてのインスタンスで同じです。Ruby:クラス変数で動作するコードを継承する

:最初、私はこのように、BまたはCではない、この

class A 
    def self.init config 
    @@config = config 
    end 

    def config 
    @@config 
    end 
end 

class B < A; end 
class C < A; end 

ようにしようとしたが、すぐに設定がAのコンテキストに保持されている@@ので、それはそのように動作しないことに気づいた時に

B.init "bar" 
p B.new.config # => "bar" 
p C.new.config # => "bar" - which would be nil if B had it's own @@config 

C.init "foo" 
p B.new.config # => "foo" - which would still be "bar" if C had it's own @@config 
p C.new.config # => "foo" 

私はこのようにそれを使用するのではと思った:それはなぜ起こるか

modules = [B, C] 
modules.each do |m| 
    m.init(@config[m.name]) 
end 
# ... 
B.new # which should then have the correct config 

は今、それは私には明らかだが、私は目についてはよく分かりませんそれがこのような理由です。

クラス変数をサブクラスのコンテキストで保持することは、他の方法でも機能しませんか?私も刺激性が自己を常にスーパークラスの中に 'と呼ばれるも、サブクラスであるということがわかった何

。これから、スーパークラスのコードがサブクラスのコンテキストで実行されることが最初に予想されました。

これについてのいくつかの啓発は非常に高く評価されます。一方

、私はおそらくそれがそのように動作し、私はこれを行うための別の方法を見つける必要があることを受け入れなければなりません。

これを行うための「メタ」方法はありますか? (私はclass_variable_setなどで頑張ってみましたが)

最初に「init」メソッドに欠陥があり、これを行うための他の「パターン」がありますか?

すべての設定を保持し、常に正しいものを選んで@@ configをハッシュにすることはできますが、少し厄介です。(この種の問題を解決するための継承はありません;)

答えて

94

@@variablesはクラス変数ではありません。それらはクラス階層変数であり、すべてのサブクラスおよびすべてのサブクラスのすべてのインスタンスを含むクラス階層全体で共有されます。 (彼らが実際に@ivarsよりも$globalsと共通の多くを持っているので、1は、より多くの$$variablesよう@@variablesを考えなければならないことが示唆されている。その方法は以下の混乱に位置しています。その他には、さらに行って、彼らは単に言語から削除する必要があることを示唆しています。 )

Rubyは、たとえば、彼らは静的フィールドと呼ばれたJava()はそれらを持っている意味でのクラス変数を持っていません。 にはのクラス変数が必要ではありません。クラスもオブジェクトなので、他のオブジェクトと同様にインスタンスの変数を持つことができます。無関係な@を削除するだけです。 (そして、クラスインスタンス変数のアクセッサメソッドを提供する必要があります。)

class A 
    def self.init config 
    @config = config 
    end 

    def self.config # This is needed for access from outside 
    @config 
    end 

    def config 
    self.class.config # this calls the above accessor on self's class 
    end 
end 

A.configは明らかにちょうどattribute_readerているので、のは、このビットを単純化してみましょう:

class A 
    class << self 
    def init config 
     @config = config 
    end 

    attr_reader :config 
    end 

    def config 
    self.class.config 
    end 
end 

そして、実際には、A.initは面白い名前を持つだけの作家である、それではA.config=に名前を変更してみましょうそれをライターにして、今度は私たちのペアのメソッドが単なるアクセサーのペアであることを意味します。 (私たちはAPIを変更したので、テストコードは明らかに、同様に変更することがあります。)

class A 
    class << self 
    attr_accessor :config 
    end 

    def config 
    self.class.config 
    end 
end 

class B < A; end 
class C < A; end 

B.config = "bar" 
p B.new.config # => "bar" 
p C.new.config # => nil 

C.config = "foo" 
p B.new.config # => "bar" 
p C.new.config # => "foo" 

をしかし、あなたがこれを必要とする場合、私は、デザインについてもっと根本的にあやふやなものがあるという感覚を振り払うことができませんまったく。

+0

デザインがどのようになっているかはわかりません。それは一般的に行うには十分な妥当なもののようです。 – Chuck

+0

これはまさに私が知る必要があったことです、ありがとう! :) 本当に他に何を言うべきか分からない、今はっきりしている。 initメソッドは複数の変数を設定することを意図していましたが、わかりやすくするためにconfigサンプルがあります。しかし今、あなたはそれを言います、それはおそらくアクセサでもっときれいでしょう;) もう一度、ありがとう! –

+0

@Chuck:たとえば、インスタンスメソッドを呼び出さず、インスタンス状態にアクセスしたり、オーバーライドしたりしないインスタンスメソッド( 'A#config')があります。それはトリミングされた例のアーティファクトかもしれませんが、合法的なデザインかもしれませんが、そうではないかもしれません。また、BとCはAから継承していますが、何もオーバーライドしませんが、それらはまったく同じですが、何らかの形でお互いにもAからも異なると予想されます。もう一度:分かりやすいかもしれません。それはすべて文脈に依存しますが、この例では賢明な結論に達するには小さすぎます。 –

関連する問題