【问题标题】:Have a parent class's method access the subclass's constants让父类的方法访问子类的常量
【发布时间】:2012-02-19 08:21:44
【问题描述】:

例如:

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 print "bark" 

我该如何完成上述任务?目前它说

uninitialized constant Animal::NOISE

【问题讨论】:

    标签: ruby constants


    【解决方案1】:

    我认为你并不真正想要一个常数;我认为你想要一个类上的实例变量:

    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"
    

    但是,如果您确定需要一个常量:

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

    【讨论】:

    • 但是如果我使用实例变量,这意味着 Dog 类的每个实例都必须存储噪声数据,对吗? Dogs 实例之间的噪音不会改变(即,Dogs 总是吠叫),所以这就是我想到子类常量的原因。你怎么看?
    • @Tim 不,Dog 类的每个实例都不存储该值。 p Dog.new.instance_eval{ @noise } #=&gt; nil 名为 DogClass 的单个实例存储该值。正如类的实例是可以具有实例变量的对象一样,类本身也是可以具有自己的实例变量的对象(Class 类的实例)。
    • 我已经进一步更新了示例以表明这些是Dog 本身的属性,而不是 Dog 实例的属性。如果您想更强烈地执行其恒定性,您甚至可以将 attr_accessor 更改为 attr_reader
    • 你不需要经过const_get。你可以做self.class::NOISE
    • 小重构:你可以使用 cattr_accessor :noise 代替 class
    【解决方案2】:

    一种不使用类实例变量的方法:

    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
    

    【讨论】:

      【解决方案3】:

      我认为你在这里有错误的概念。 Ruby 中的类与 Java、Smalltalk、C# 中的类相似,......并且都是它们实例的模板。因此,类定义了其实例的结构和行为,以及其子类实例的部分结构和行为但反之则不然

      所以根本不可能从超类直接访问子类中的常量,这是一件好事。请参阅下文如何修复它。对于您定义的类,以下情况属实:

      • class Animal 定义了方法 make_noise
      • class Animal 的实例可以调用方法make_noise
      • class Dog定义常量NOISE及其值。
      • Dog 的实例和Dog 本身的类可以使用常量NOISE

      什么是不可能的:

      • Animal 或类Animal 本身的实例可以访问Dog 类的常量。

      您可以通过以下更改来解决此问题:

      class Animal
        def make_noise
          print Dog::NOISE
        end
      end
      

      但这是不好的风格,因为现在,你的超类(它是关于 Dog 和其他动物的抽象)现在知道属于 Dog 的东西。

      更好的解决方案是:

      1. 在类Animal 中定义一个抽象方法,该方法定义应定义make_noise。查看答案https://stackoverflow.com/a/6792499/41540
      2. 在您的具体类中再次定义该方法,但现在使用对常量的引用。

      【讨论】:

      • 其实是可以的;看我的回答。
      • 我看过你的回答(后来),和我的不一样。这是一个很好的技巧,但在超类中访问子类的常量的方式很糟糕......
      • 我同意这是一种糟糕的风格,我一般不会批评您的回答。只是你说“所以你想要达到的东西根本不可能。”
      【解决方案4】:

      如果您这样做是为了配置子类,以便基类可以访问常量,那么您可以为它们创建一个 DSL,如下所示:

      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
      

      这种方式的性能更高一点,因为在每个方法调用中,您不必自省类以返回(或向前?)常量。

      【讨论】:

        【解决方案5】:

        如果你想要面向对象的方式 (TM),那么我猜你想要:

        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
        

        如果你想重构以防止重复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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-02-04
          • 2023-04-01
          • 2017-07-15
          • 2012-07-04
          • 2020-02-13
          • 1970-01-01
          • 1970-01-01
          • 2014-10-04
          相关资源
          最近更新 更多