Модуль включения / расширения Ruby: метод класса - Начинающий - PullRequest
2 голосов
/ 19 октября 2019

Я читал эту статью о разнице между include & extend в рубине.

Если у меня есть этот модуль, я понимаю, как первый и второй методымодуля будет использоваться в классе. Что я не понимаю, так это то, как class << self будет использоваться include или extend.

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

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

Теперь проблема заключается в выполнении Car.stop или Car.new.stop всегда приведет к ошибке:

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

Почему методы класса не переносятся через include и extend?


Я начал думать об этом из-за своего исследования [forwardable исходного кода в строке 119]. (https://github.com/ruby/ruby/blob/master/lib/forwardable.rb#L119)

Спасибо за любую помощь, которая может у вас быть!


Обновление от ответа ниже

Ниже приводится пример, приведенный ниже:

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

Теперь я понимаю это и понимаю, какЯ могу реализовать методы класса из модуля в класс, используя def self.included(base). Мой вопрос заключается в том, что если бы мы использовали extend внутри Car вместо include, мы все равно смогли бы получить эти методы класса, используя def self.included(base) * * 1045

1 Ответ

3 голосов
/ 19 октября 2019

Когда вы определяете метод с помощью class << self, вы определяете метод класса. Это то же самое, что и определение метэда следующим образом:

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

Выше показано 2 способа определения методов класса, которые вызываются непосредственно в классе, а не в экземплярах класса. Вы можете использовать второй синтаксис, если хотите определить только методы класса или несколько из них внутри блока class << self. Но любой стиль имеет одинаковый результат.

Поскольку вы определили метод класса в модуле Direction, include или extend не будут наследовать метод класса этого модуля. Это ожидаемое поведение.

Если вы хотите использовать наследование с методами класса из модуля, вы должны сделать это так, как описано ниже в статье, которую вы связали

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

Теперь вызывая методы классаCar наследует, как определено в классе Direction.

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

Однако всегда будьте осторожны при использовании любого рода наследования, поскольку Ruby является динамическим языком. Поэтому, если вы выполните приведенный выше код, а затем переопределите этот метод:

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

Угадайте, что произойдет, если вы сделаете это:

Car.stop

Поскольку метод был определен внутри Directionмодуль, когда метод вызывается Car, он будет вызывать метод из модуля Direction.

Car.stop
go!
=>nil

Обновлен на основе комментариев:

Если вы предпочитаете использовать extend против include вместо этого вам нужно будет сделать следующее:

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

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

class Car
  extend Direction
end

В этом примере все методы, которые были унаследованы от модуля, «копируются» в класс, расширяющий их. Это позволяет избежать проблемы возможного результата переопределения метода модуля, о котором я предупреждал при использовании include ранее в моем ответе.

Но вы можете посмотреть ответы на на этот вопрос , чтобы узнать, когда и зачем использовать тот или иной случай.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...