2013-10-15 27 views
6

Załóżmy, że mam klasę z kilkoma zmiennymi "statycznymi". Chcę podklas tej klasy, aby móc zastąpić te zmienne bez wpływu na oryginalną klasę. Nie jest to możliwe przy użyciu zmiennych klasowych, gdyż te wydają się być dzielone między podklasy i superklas:Jak przesłonić zmienną w podklasie Ruby bez wpływu na nadklasę?

class Foo 
    @@test = "a" 

    def speak; puts @@test; end 
end 

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

Bar.new.speak 
# b 

Foo.new.speak 
# b 

To nie jest możliwe przy użyciu stałych albo:

class Foo 
    TEST = "a" 

    def speak; puts TEST; end 
end 

class Bar < Foo 
    TEST = "b" 
end 

Bar.new.speak 
# a 

Foo.new.speak 
# a 

Metody zdefiniowane w nadrzędnej ignoruje stałych w podklasie.

Oczywistym rozwiązaniem jest zdefiniowanie metod dla zmiennych, które muszą być „przeciążać”:

class Foo 
    def test; "a"; end 
end 

jednak, że czuje się jak hack. Czuję, że powinno to być możliwe przy użyciu zmiennych klasowych i prawdopodobnie robię to źle. Na przykład, kiedy podklasy Object (czyli to, co dzieje się domyślnie):

class Foo < Object 
    @@bar = 123 
end 

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

Dlaczego nie jest @@bar zestaw na Object jak to było w moim Bar < Foo powyższym przykładzie?


Podsumowując: w jaki sposób przesłonić zmienną w podklasie bez wpływu na nadklasę?

Odpowiedz

5

stałe Class robi to, co chcesz, po prostu trzeba je wykorzystać w różny sposób:

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

Edytował tekst odpowiedzi, aby zachować więcej sensu, mam nadzieję, że nie masz nic przeciwko. Wydaje mi się, że jest to najbardziej poprawna metoda, więc akceptuję odpowiedź. – Hubro

+0

@Codemonkey Pierwotne pytanie dotyczyło zmiennych, a nie stałych. Chociaż Ruby pozwala nadpisywać stałe, otrzymasz ostrzeżenia o tym i będzie to mylące dla każdego czytającego twój kod. Odpowiedź Denisa jest właściwym sposobem implementacji zmiennych na klasy. – Max

+1

@Max: Jeśli przeczytasz całe pytanie, będzie jasne, że wszystko, co robię, jest sposobem na przesłonięcie wartości * związanej z klasą w podklasie - nie ma znaczenia, czy są to zmienne czy stałe. Zgadzam się, że "zmienna" mogła być złym wyborem słów. Używanie takich stałych wydaje mi się prostsze i bardziej intuicyjne niż definiowanie czytników atrybutów meta-klasy. Nie otrzymałem żadnych ostrzeżeń od definiowania stałych w podklasach o tej samej nazwie, co stałe w ich nadklasach (jak w tym przykładzie odpowiedzi). – Hubro

2

Właściwy sposób (IMHO) polega na użyciu metod, ponieważ w ten sposób używasz dziedziczenia i wirtualnego wysyłania, tak jak chcesz.

Zmienne klasy są udostępniane w hierarchii, a nie w górę. Dlatego @@bar nie jest dostępny w Object.

+0

+1 dla wyjaśnienia zmiennych klas – Hubro

4

dodać zmienną do samej klasy (jak na przykład klasy, zamiast zmiennej klasy):

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

Podoba mi się to podejście, chociaż! –

+0

To też działa: 'Foo.var # A' i' Bar.var # B' –

Powiązane problemy