2012-01-26 15 views

Respuesta

26

Creo que realmente no quieres una constante; Creo que desea una variable de instancia de la clase:

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" 

Sin embargo, si está seguro de que desea una constante:

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

Pero si utilizo una variable de instancia, eso significa que cada instancia de la clase Dog tendrá que almacenar los datos de ruido, ¿no? El ruido no cambiará entre las instancias de los perros (es decir, los perros siempre ladran), así que es por eso que pensé en una constante de subclase. ¿Qué piensas? – Tim

+0

@Tim No, cada instancia de la clase Dog no almacena ese valor. 'p Dog.new.instance_eval {@noise} # => nil' La única instancia de la' Clase 'que se llama 'Dog' almacena ese valor. Así como las instancias de clases son objetos que pueden tener variables de instancia, las mismas clases son objetos (instancias de la clase Class) que pueden tener sus propias variables de instancia. – Phrogz

+0

He actualizado el ejemplo para mostrar que estas son propiedades de 'Perro' en sí, no propiedades de instancias de Perro. Incluso podría cambiar 'attr_accessor' por' attr_reader' si quisiera reforzar su constancia. – Phrogz

0

Creo que tienen el concepto equivocado aquí. Las clases en Ruby son similares a las clases en Java, Smalltalk, C#, ... y todas son plantillas para sus instancias. Entonces la clase define la estructura y el comportamiento si sus instancias, y las partes de la estructura y el comportamiento de las instancias de sus subclases pero no viceversa.

Así que el acceso directo de una superclase a una constante en una subclase no es posible en absoluto, y eso es algo bueno. Vea a continuación cómo solucionarlo. Para sus clases definidas, las siguientes cosas son ciertas:

  • class Animal define el método make_noise.
  • instancias de class Animal pueden llamar al método make_noise.
  • class Dog define la constante NOISE con su valor.
  • instancias de Dog y la clase Dog puede usar la constante NOISE.

Lo que no es posible:

  • Las instancias de la clase o AnimalAnimal sí tienen acceso a las constantes de la clase Dog.

Usted puede arreglar eso por el siguiente cambio:

class Animal 
    def make_noise 
    print Dog::NOISE 
    end 
end 

Pero esto es un mal estilo, porque ahora, su superclase (que es una abstracción sobre Dog y otros animales) sabe ahora algo que pertenece a Dog.

Una mejor solución sería:

  1. definir un método abstracto en la clase Animal cuales define que make_noise deberían definirse. Ver la respuesta https://stackoverflow.com/a/6792499/41540.
  2. Defina en sus clases concretas el método de nuevo, pero ahora con la referencia a la constante.
+0

En realidad, es posible; ver mi respuesta – Phrogz

+0

He visto tu respuesta (después), y es diferente a la mía. Es un buen truco, pero un mal estilo para acceder a las constantes de las subclases en las superclases ... – mliebelt

+0

Estoy de acuerdo en que es una especie de mal estilo, y no estoy criticando tu respuesta en general. Justo lo que dices _ "Entonces, lo que quieres alcanzar no es posible en absoluto". _ – Phrogz

4

una manera de hacerlo sin variables de instancia de clase:

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

Si desea que el Camino orientada a objetos (TM), entonces supongo que desee:

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 

Si desea refactorizar para evitar la duplicación del código de 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

Si usted está haciendo esto para configurar sus subclases para que la clase base tiene acceso a las constantes continuación, puede crear un DSL para ellos de esta manera:

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 

De esta manera es un poco más performante como en cada Método de llamada no es necesario introspeccionar la clase para volver atrás (¿o es hacia adelante?) para la constante.

Cuestiones relacionadas