Have a parent class's method access the subclass's constants

前端 未结 5 814
闹比i
闹比i 2021-02-12 03:56

For example:

class Animal

 def make_noise
    print NOISE
 end

end

class Dog < Animal
    NOISE = \"bark\"
end

d = Dog.new
d.make_noise # I want this to p         


        
相关标签:
5条回答
  • 2021-02-12 04:16

    I think that you don't really want a constant; I think that you want an instance variable on the class:

    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"
    

    However, if you are sure that you want a constant:

    class Animal
      def make_noise
        puts self.class::NOISE
        # or self.class.const_get(:NOISE)
      end
    end
    
    0 讨论(0)
  • 2021-02-12 04:24

    If you're doing this to configure your sub classes so that the base class has access to the constants then you can create a DSL for them like this:

    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
    

    This way is a bit more performant as on each method call you don't have to introspect the class to reach back (or is it forward?) for the constant.

    0 讨论(0)
  • 2021-02-12 04:36

    one way to do it without class instance variables:

    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
    
    0 讨论(0)
  • I think you have the wrong concept here. Classes in Ruby are similar to classes in Java, Smalltalk, C#, ... and all are templates for their instances. So the class defines the structure and the behavior if its instances, and the parts of the structure and behavior of the instances of its subclasses but not vice versae.

    So direct access from a superclass to a constant in a subclass is not possible at all, and that is a good thing. See below how to fix it. For your classes defined, the following things are true:

    • class Animal defines the method make_noise.
    • instances of class Animal may call the method make_noise.
    • class Dogdefines the constant NOISE with its value.
    • instances of Dog and the class Dog itself may use the constant NOISE.

    What is not possible:

    • Instances of Animal or the class Animal itself have access to constants of the class Dog.

    You may fix that by the following change:

    class Animal
      def make_noise
        print Dog::NOISE
      end
    end
    

    But this is bad style, because now, your superclass (which is an abstraction about Dog and other animals) knows now something that belongs to Dog.

    A better solution would be:

    1. Define an abstract method in class Animal which defines that make_noise should be defined. See the answer https://stackoverflow.com/a/6792499/41540.
    2. Define in your concrete classes the method again, but now with the reference to the constant.
    0 讨论(0)
  • 2021-02-12 04:41

    If you want it the Object-Oriented Way (TM), then I guess you want:

    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
    

    If you want to refactor to prevent duplicating the code for 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 讨论(0)
提交回复
热议问题