简体   繁体   English

Ruby 包含/扩展模块:class 方法 - 初学者

[英]Ruby include/extend Module: a class method - Beginner

I've been reading this article on the difference between include & extend in ruby.我一直在阅读这篇关于 ruby 中includeextend之间区别的文章。

If I have this module, I understand how the first and second methods of the module will be used in the class.如果我有这个模块,我了解模块的第一种和第二种方法将如何在 class 中使用。 What I don't understand is how the class << self will be used by include or extend .我不明白class << self将如何被includeextend使用。

module Direction
  def straight
    puts "going straight!"
  end

  def turn
    puts "turning!"
  end

  class << self
    def stop
      puts "stopping!"
    end
  end
end

# This will work because `include` brings them in as instance methods
class Car
  include Direction
end

Car.new.straight
Car.new.turn

# ---------------------
# Now this will also work because `extend` brings them in as class methods
class Car
  extend Direction
end

Car.straight
Car.turn

# ---------------------

Now, the issue is, doing Car.stop or Car.new.stop will always result in an error:现在,问题是,执行Car.stopCar.new.stop总是会导致错误:

/Users/<name>/Projects/ruby-testing/main.rb:34:in `<main>': undefined method `stop' for Car:Class (NoMethodError)

Why are class methods not carried over via include and extend ?为什么 class 方法不通过includeextend进行?


I started thinking about this because of my research into the [ forwardable source code at line 119].( https://github.com/ruby/ruby/blob/master/lib/forwardable.rb#L119 )由于我对 [第 119 行的可forwardable源代码] 的研究,我开始考虑这个问题。( https://github.com/ruby/ruby/blob/master/lib/forwardable.rb#L119

Thank you for any help you may have!感谢您提供的任何帮助!


Update from Answer Below从下面的答案更新

The following was an example given:以下是给出的示例:

module Direction
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def stop
      puts 'stopping!'
    end
  end

  def straight
    puts "going straight!"
  end

  def turn
    puts "turning!"
  end
end

class Car
  include Direction
end

This I understand now, and I understand how I can implement class methods from a module into a class using def self.included(base) .我现在明白了,我明白了如何使用def self.included(base)将 class 方法从模块实现到 class 。 My question is, if we used extend inside of Car instead of include , would we still be able to get at those class methods using def self.included(base) ?我的问题是,如果我们在Car内部使用extend而不是include ,我们是否仍然能够使用def self.included(base)获得那些 class 方法?

When you define a method with class << self you are defining a class method.当您使用class << self定义方法时,您正在定义 class 方法。 It's the same as defining the methed like this:这与定义这样的方法相同:

class Foo
  def self.foo
    puts 'foo'
  end
  # the above definition is the same as doing:
  class << self
    def foo
      puts 'foo'
    end
  end
end

The above shows 2 ways of defining class methods which are called directly on the class and not on instances of the class.上面显示了定义 class 方法的 2 种方法,这些方法直接在 class 上调用,而不是在 class 的实例上调用。 You might use the 2nd syntax if you want to define only class methods or several of them inside of the class << self block.如果您只想定义 class 方法或class << self块内的几个方法,则可以使用第二种语法。 But either style has the same result.但任何一种风格都有相同的结果。

Since you've defined a class method on the Direction module, include or extend will not inherit the class method of that module.由于您在Direction模块上定义了 class 方法,因此includeextend不会继承该模块的 class 方法。 This is the expected behavior.这是预期的行为。

If you want to use inheritance with class methods from a module, you should do it like this which is explained further down in the article you've linked如果你想使用 inheritance 和模块中的 class 方法,你应该这样做,这在你链接的文章中进一步解释

module Direction
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def stop
      puts 'stopping!'
    end
  end

  def straight
    puts "going straight!"
  end

  def turn
    puts "turning!"
  end
end

class Car
  include Direction
end

Now calling class methods on Car will inherit as defined in the Direction class.现在在Car上调用 class 方法将继承Direction class 中定义的方法。

Car.stop
stopping!
=>nil # calling a method will return nil unless the method returns a value.

However always be careful using inheritance of any kind as Ruby is a dynamic language.但是,请务必小心使用任何类型的 inheritance,因为 Ruby 是一种动态语言。 So if you do the above code and then later redefine this method:所以如果你做上面的代码,然后再重新定义这个方法:

module Direction
  module ClassMethods
    def stop
      puts 'go!'
    end
  end
end

Guess what will happen if you do this:猜猜如果你这样做会发生什么:

Car.stop

Since the method was defined inside Direction module, when the method gets called on Car it will be calling the method from the Direction module.由于该方法是在Direction模块中定义的,因此当在Car上调用该方法时,它将从Direction模块调用该方法。

Car.stop
go!
=>nil

Updated based on comments:根据评论更新:

If you prefer to use extend vs include you would need to do this instead:如果您更喜欢使用extendinclude ,则需要这样做:

module Direction
  def self.extended(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def stop
      puts 'stopping!'
    end
  end
end

class Car
  extend Direction
end

In this example, all the methods which were inherited from the module are "copied" to the class extending them.在此示例中,从模块继承的所有方法都“复制”到扩展它们的 class。 This avoids the problem of possible result of redefining the module method which I warned about when using include previously in my answer.这避免了重新定义模块方法的可能结果问题,我在之前的答案中使用include时警告过该方法。

But you may want to look at answers to this question for ideas about when and why to use either case.但是您可能想查看这个问题的答案,以了解何时以及为什么使用这两种情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM