2013-10-15 8 views
6

Nehmen wir an, ich habe eine Klasse mit ein paar "statischen" Variablen. Ich möchte eine Unterklasse dieser Klasse in der Lage sein, diese Variablen zu überschreiben, ohne die ursprüngliche Klasse zu beeinflussen. Dies ist nicht möglich, Klassenvariablen verwenden, da diese erscheint zwischen Subklassen und Super geteilt werden:Wie überschreibe ich eine Variable in einer Ruby-Unterklasse, ohne die Oberklasse zu beeinflussen?

class Foo 
    @@test = "a" 

    def speak; puts @@test; end 
end 

class Bar < Foo 
    @@test = "b" 
end 

Bar.new.speak 
# b 

Foo.new.speak 
# b 

Es ist nicht möglich Konstanten entweder:

class Foo 
    TEST = "a" 

    def speak; puts TEST; end 
end 

class Bar < Foo 
    TEST = "b" 
end 

Bar.new.speak 
# a 

Foo.new.speak 
# a 

Methoden in der übergeordneten Klasse definiert Konstanten ignoriert in der Unterklasse.

Das offensichtliche Problem zu umgehen ist es, Methoden für die Variablen zu definieren, die „außer Kraft gesetzt“ sein muss:

class Foo 
    def test; "a"; end 
end 

Aber das fühlt sich an wie ein Hack. Ich denke, das sollte mit Klassenvariablen möglich sein und ich mache es wahrscheinlich nur falsch. Zum Beispiel, wenn ich eine Unterklasse Object (das ist, was passiert, in der Standardeinstellung):

class Foo < Object 
    @@bar = 123 
end 

Object.class_variable_get(:@@bar) 
# NameError: uninitialized class variable @@bar in Object 

Warum auf Object nicht @@bar Satz ist wie es in meinem Bar < Foo Beispiel oben war?


Zusammengefasst: Wie überschreibe ich eine Variable in einer Unterklasse, ohne die Oberklasse zu beeinflussen?

Antwort

5

Klasse Konstanten tut, was Sie wollen, müssen Sie nur sie anders nutzen:

class Foo 
    TEST = "a" 

    def speak 
    puts self.class::TEST 
    end 
end 

class Bar < Foo 
    TEST = "b" 
end 

Bar.new.speak # => a 
Foo.new.speak # => b 
+0

Bearbeitete den Text der Antwort, um mehr Sinn zu machen, hoffe, dass es Ihnen nichts ausmacht. Das scheint mir der richtige Weg zu sein, also akzeptiere ich die Antwort. – Hubro

+0

@Codemonkey Ihre ursprüngliche Frage betraf Variablen, keine Konstanten. Obwohl Sie in Ruby Konstanten überschreiben können, erhalten Sie Warnungen, und das wird für jeden, der Ihren Code liest, verwirrend sein. Denis 'Antwort ist die richtige Art, Variablen pro Klasse zu implementieren. – Max

+1

@Max: Wenn Sie die ganze Frage lesen, wird klar, dass alles, was ich bin, eine Möglichkeit ist, einen * Wert * zu überschreiben, der an eine Klasse in einer Unterklasse gebunden ist - es ist nicht wichtig, ob es Variablen oder Konstanten sind. Ich stimme zu, dass "Variable" eine schlechte Wortwahl gewesen sein könnte. Die Verwendung solcher Konstanten scheint mir einfacher und intuitiver zu sein als die Definition von Meta-Klasse-Attribut-Lesern. Ich habe keine Warnungen bekommen, Konstanten in Unterklassen mit dem gleichen Namen wie Konstanten in ihren Oberklassen zu definieren (wie im Beispiel dieser Antwort). – Hubro

2

Der richtige Weg (IMHO) ist die Verwendung von Methoden, weil Sie auf diese Weise Vererbung und virtuelles Dispatching verwenden, ganz wie Sie möchten.

Klassenvariablen werden in der Hierarchie geteilt, nicht oben. Deshalb ist @@bar in Object nicht verfügbar.

+0

+1 für die Erklärung zu Klassenvariablen – Hubro

4

eine Variable der Klasse hinzufügen selbst (wie in der Klasseninstanz, sondern als eine Klassenvariable):

class Foo 
    @var = 'A' 

    class << self 
    attr_reader :var 
    end 

    def var 
    self.class.var 
    end 
end 

class Bar < Foo 
    @var = 'B' 
end 

Foo.var # A 
Foo.new.var # A 
Bar.var # B 
Bar.new.var # B 
+0

Ich mag diesen Ansatz, obwohl! –

+0

Das funktioniert auch: 'Foo.var # A' und' Bar.var # B' –

Verwandte Themen