2012-01-26 9 views

risposta

26

Penso che non si voglia veramente una costante; Penso che si desidera una variabile di istanza sulla classe:

class Animal 
    @noise = "whaargarble" 
    class << self 
    attr_accessor :noise 
    end 
    def make_noise 
    puts self.class.noise 
    end 
end 

class Dog < Animal 
    @noise = "bark" 
end 

a = Animal.new 
d = Dog.new 
a.make_noise #=> "whaargarble" 
d.make_noise #=> "bark" 
Dog.noise = "WOOF" 
d.make_noise #=> "WOOF" 
a.make_noise #=> "whaargarble" 

Tuttavia, se si è certi che si desidera una costante:

class Animal 
    def make_noise 
    puts self.class::NOISE 
    # or self.class.const_get(:NOISE) 
    end 
end 
+0

Ma se uso una variabile di istanza, significa che ogni istanza della classe Dog dovrà memorizzare i dati di rumore, giusto? Il rumore non cambierà tra le istanze di Cani (cioè, i cani abbaiano sempre), ecco perché ho pensato a una costante di sottoclasse. Cosa ne pensi? – Tim

+0

@Tim No, ogni istanza della classe Dog non memorizza quel valore. 'p Dog.new.instance_eval {@noise} # => nil' La singola istanza della' Class' che si chiama 'Dog' memorizza quel valore. Proprio come le istanze delle classi sono oggetti che possono avere variabili di istanza, quindi le classi stesse sono oggetti (istanze della classe Class) che possono avere le proprie variabili di istanza. – Phrogz

+0

Ho aggiornato ulteriormente l'esempio per mostrare che queste sono proprietà di 'Dog' stesso, non proprietà di istanze di Dog. Si potrebbe anche cambiare 'attr_accessor' in' attr_reader' se si volesse rafforzare maggiormente la sua costanza. – Phrogz

0

penso che tu abbia il concetto sbagliato qui. Le classi in Ruby sono simili alle classi in Java, Smalltalk, C#, ... e tutti sono modelli per le loro istanze. Quindi la classe definisce la struttura e il comportamento se le sue istanze e le parti della struttura e il comportamento delle istanze delle sue sottoclassi ma non viceversa.

Quindi l'accesso diretto da una superclasse a una costante in una sottoclasse non è affatto possibile e questa è una buona cosa. Vedi sotto come risolverlo. Per le tue classi definite, le seguenti cose sono vere:

  • class Animal definisce il metodo make_noise.
  • istanze di class Animal possono chiamare il metodo make_noise.
  • class Dog definisce la costante NOISE con il suo valore.
  • istanze di Dog e la classe Dog può utilizzare la costante NOISE.

Quello che non è possibile:

  • istanze di Animal o la classe Animal sé hanno accesso alle costanti della classe Dog.

Si può rimediare con la seguente modifica:

class Animal 
    def make_noise 
    print Dog::NOISE 
    end 
end 

Ma questo è un cattivo stile, perché ora, la tua superclasse (che è un'astrazione su Dog e altri animali) sa ormai qualcosa che appartiene a Dog.

Una soluzione migliore sarebbe:

  1. Definire un metodo astratto nella classe Animal quali definisce che make_noise occorre definire. Vedere la risposta https://stackoverflow.com/a/6792499/41540.
  2. Definisci di nuovo il metodo nelle tue classi concrete, ma ora con il riferimento alla costante.
+0

In realtà, è possibile; guarda la mia risposta – Phrogz

+0

Ho visto la tua risposta (in seguito), ed è diversa dalla mia. È un bel trucco, ma uno stile pessimo per accedere alle costanti delle sottoclassi nelle superclassi ... – mliebelt

+0

Concordo sul fatto che sia una specie di cattivo stile, e non sto criticando la tua risposta in generale. Solo il punto in cui dici _ "Quindi ciò che vuoi raggiungere non è affatto possibile." _ – Phrogz

4

un modo per farlo senza le variabili di istanza di classe:

class Animal 

def make_noise 
    print self.class::NOISE 
end 

end 

class Dog < Animal 
    NOISE = "bark" 
end 

d = Dog.new 
d.make_noise # prints bark 
-1

Se si vuole che la Via Object-Oriented (TM), allora credo che si desidera:

class Animal 
    # abstract animals cannot make a noise 
end 

class Dog < Animal 
    def make_noise 
    print "bark" 
    end 
end 

class Cat < Animal 
    def make_noise 
    print "meow" 
    end 
end 

d = Dog.new 
d.make_noise # prints bark 

c = Cat.new 
c.make_noise # prints meow 

Se si desidera refactoring per evitare la duplicazione del codice per print:

class Animal 
    def make_noise 
    print noise 
    end 
end 

class Dog < Animal 
    def noise 
    "bark" 
    end 
end 

class Cat < Animal 
    def noise 
    if friendly 
     "meow" 
    else 
     "hiss" 
    end 
    end 
end 

d = Dog.new 
d.make_noise # prints bark 

c = Cat.new 
c.make_noise # prints meow or hiss 
0

Se stai facendo questo per configurare le classi secondarie in modo che la classe di base ha accesso alle costanti quindi è possibile creare una connessione DSL per loro in questo modo:

module KlassConfig 
    def attr_config(attribute) 
    define_singleton_method(attribute) do |*args| 
     method_name = "config_#{attribute}" 
     define_singleton_method method_name do 
     args.first 
     end 
     define_method method_name do 
     args.first 
     end 
    end 
    end 
end 

class Animal 
    extend KlassConfig 
    attr_config :noise 

    def make_noise 
    puts config_noise 
    end 
end 

class Dog < Animal 
    noise 'bark' 
end 

In questo modo è un po 'più performante come su ogni metodo di chiamata non devi introspezionare la classe per tornare indietro (o è avanti?) per la costante.

Problemi correlati