Входят ли операторы Ruby «require» внутри или вне определения класса?

При использовании файлов классов в Ruby вы размещаете операторы «требует» в верхней части файла или внутри определения класса?


person Ash    schedule 03.03.2009    source источник


Ответы (7)


Технически это не имеет большого значения. require — это обычный вызов метода, и область, в которой он вызывается, не влияет на то, как он работает. Единственная разница в размещении заключается в том, что оно будет выполнено, когда будет оцениваться любой код, в который оно помещено.

С практической точки зрения, вы должны поместить их вверху, чтобы люди могли сразу увидеть зависимости файла. Это традиционное место для него.

person Chuck    schedule 03.03.2009
comment
Означает ли это, что вы можете сделать приложение Ruby более «эффективным» (меньше памяти, более быстрое время выполнения — да, я знаю, что require в любом случае очень быстры, но...), только выполнив вызов метода require if он тебе точно нужен? - person Ash; 11.04.2014
comment
@Ash: я бы не сказал, что это особенно вероятно. Это возможно, но в большинстве случаев штраф за выполнение require каждый раз при вызове метода перевешивает любую экономию, на которую вы можете надеяться. Я делал это пару раз, но только при довольно странных обстоятельствах. - person Chuck; 11.04.2014
comment
@Ash: Кроме того, что более важно, это вредит читабельности. Если файл так дорого требует, это, вероятно, проблема сама по себе. Как правило, не стоит вредить читабельности. - person Chuck; 11.04.2014
comment
Я не делал этого во время этого вопроса, но совсем недавно у меня было несколько классов провайдеров, созданных в конце файла конфигурации. Я помещал требования в цикл экземпляра, чтобы не пытаться «требовать» файл, который не использовался. Я решил, что для производительности будет лучше загружать только то, что мне действительно нужно, но звучит так, что для будущего обслуживания может быть лучше, если я все равно просто включу их все и не буду беспокоиться о незначительном потреблении памяти и процессора, которое я могу понести. Это то, что вы говорите? - person Ash; 11.04.2014
comment
@Chuck Я не очень разбираюсь в Ruby, но нашел эту статью, которая противоречит что ты сказал. По сути, насколько я могу судить, Ruby потребует I файла только один раз, даже если это требование вызывается несколько раз. Я согласен, что лучше всего поставить require в начало файла, но технически это не повлияет на время выполнения. Теперь, я думаю, если бы вы использовали load, это была бы другая история. - person Christian Juth; 18.03.2017
comment
@ChristianJuth: Это правильно — файл будет загружен только один раз. Я не думаю, что это противоречит моему ответу. Если вы говорите о штрафе за выполнение require в моем комментарии, я говорил о штрафе за фактическое выполнение require, что означает, что Ruby должен разрешить вызов метода, выяснить, что на самом деле требуется, и проверить, загружено ли оно уже. - person Chuck; 18.03.2017
comment
@ Чак, да, это то, что я имел в виду. Это имеет смысл. - person Christian Juth; 18.03.2017

наверху.

require 'rubygems'
require 'fastercsv'

class MyClass
  # Do stuff with FasterCSV
end
person ewakened    schedule 03.03.2009

Я вижу возможную причину того, что require не помещается в начало файла: это дорого для загрузки и не всегда выполняется. Мне приходит в голову один случай, когда, например, код и его тесты находятся в одном файле, что мне нравится делать время от времени, в частности, для кода небольшой библиотеки. Затем я могу запустить файл из своего редактора, и тесты запустятся. В этом случае, когда файл required из другого места, я не хочу, чтобы test/unit загружался.

Что-то вроде этого:

def some_useful_library_function()
  return 1
end

if __FILE__ == $0
  require 'test/unit'
  class TestUsefulThing < Test::Unit::TestCase
    def test_it_returns_1
      assert_equal 1, some_useful_library_function()
    end
  end
end
person Mike Woodhouse    schedule 03.03.2009

На самом деле не имеет значения, куда вы их поместите, но если вы поместите их внутри выражения class или module, то будет выглядеть так, как будто вы импортируете все, что находится в файле required, в пространство имен класса, то есть неверно: все попадает в глобальное пространство имен (или любое другое пространство имен, определенное в библиотеке).

Так что лучше поместите их вверху, чтобы избежать путаницы.

person Jörg W Mittag    schedule 03.03.2009

В верхней части файла большинство (но не все) языков обрабатывают импорт таким образом. Я нахожу, что так намного чище и проще обращаться с ними.

Я думаю, что на самом деле это имеет смысл только так... как будто вы в середине файла:

class Foo
  def initialize(init_value)
    @instance_var = init_value

# some 500 lines of code later....

  end
end

class Bar
# oh look i need an import now!
require 'breakpoint'

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

person John T    schedule 03.03.2009
comment
Это может не увеличивать накладные расходы. Не уверен насчет ruby, но импорт других модулей в python кэшируется. - person Matthew Schinckel; 03.03.2009

Я чувствую, что оператор require принадлежит классу. Использование классов означает, что мы принимаем основной принцип ООП, а именно, объекты должны быть как можно более слабо связаны. Для меня это означает минимизацию внешних зависимостей. Если позже я перемещу класс в его собственный файл, я не хочу, чтобы он сломался, потому что я не отследил все необходимые операторы require, которые использует класс.

Дублирование операторов require в файле не создает проблем и упрощает рефакторинг, который неизбежно будет выполнен следующим программистом, унаследовавшим ваш код.

person WiiBopp    schedule 14.04.2016

В большинстве ответов рекомендуется помещать операторы require в начало файла. Однако, когда требуются вложенные классы/модули, вы можете вместо этого рассмотреть возможность их размещения в классе (вверху). Взгляните на этот пример:

# foo.rb
require './foo/bar'

class Foo < Struct.new(:name, :description)
  def bar
    Bar.new(self)
  end
end

# foo/bar.rb
class Foo
  class Bar < Struct.new(:foo)
  end
end

irb:

require './foo'
# ...
# TypeError (superclass mismatch for class Foo)

Это происходит потому, что Bar вложен в Foo, и его необходимо определить как таковой, вложив класс Bar внутрь Foo класс. Однако, поскольку Foo еще не определен, теперь он определяется вложенной структурой. После того, как Bar успешно потребовался, мы пытаемся определить класс Foo, который наследуется от другого класса. Это не удается из-за того, что Foo уже определен (вложенной структурой) и наследование может происходить только в исходном определении класса. Таким образом поднимая:

TypeError (несоответствие суперкласса для класса Foo)

Эту проблему можно решить, просто переместив оператор require внутрь класса Foo.

person 3limin4t0r    schedule 19.02.2019