Вопрос по ruby – Рекомендуемый подход к исправлению класса обезьяны в ruby

27

Я заметил, что существует два распространенных способа исправления обезьяны классом в ruby:

Определите новых членов класса следующим образом:

<code>class Array
   def new_method
     #do stuff
   end
end
</code>

И вызов class_eval для объекта класса:

<code>Array.class_eval do
   def new_method
      #do stuff
   end
end
</code>

Мне интересно, есть ли разница между ними и есть ли преимущества в использовании одного подхода по сравнению с другим?

Возможный дубликатmonkey patching vs class_eval? akostadinov

Ваш Ответ

2   ответа
56

Честно говоря, я использовал 1-ю форму (повторное открытие класса), так как она кажется более естественной, но ваш вопрос заставил меня провести некоторое исследование по этому вопросу, и вот результат.

Проблема с повторным открытием класса состоит в том, что он будет молча определять новый класс, если исходный, который вы хотели открыть, по какой-то причине не был определен в данный момент. Результат может быть другим:

  1. If you don't override any methods but only add the new ones and the original implementation is defined (e.g., file, where the class is originally defined is loaded) later everything will be ok.

  2. If you redefine some methods and the original is loaded later your methods will be overridden back with their original versions.

  3. The most interesting case is when you use standard autoloading or some fancy reloading mechanism (like the one used in Rails) to load/reload classes. Some of these solutions rely on const_missing that is called when you reference undefined constant. In that case autoloading mechanism tries to find undefined class' definition and load it. But if you're defining class on your own (while you intended to reopen already defined one) it won't be 'missing' any longer and the original might be never loaded at all as the autoloading mechanism won't be triggered.

С другой стороны, если вы используетеclass_eval Вы будете немедленно уведомлены, если класс не определен в данный момент. Кроме того, когда вы ссылаетесь на класс, когда вы вызываете егоclass_eval метод, любой механизм автозагрузки будет иметь возможность найти класс & apos; определение и загрузить его.

Имея это в видуclass_eval кажется, лучший подход. Хотя я был бы рад услышать другое мнение.

В конце концов, Google - довольно мощный инструмент =)
Хорошее исследование :)
7

Scope

Одна большая разница, на которую, я думаю, KL-7 не указал, это область, в которой будет интерпретироваться ваш новый код:

Если вы (повторно) открываете класс для манипулирования им, новый код, который вы добавляете, будет интерпретироваться в области действия (оригинального) класса.
Если вы используетеМодуль # class_eval чтобы манипулировать классом, новый код, который вы добавляете, будет интерпретироваться в области видимости вашего вызова #class_eval и НЕ будет знать о области действия класса. Если кто-то не знает, это поведение может быть нелогичным и привести к трудным для отладки ошибкам.

CONSTANT    = 'surrounding scope'

# original class definition (uses class scope)
class C
  CONSTANT  = 'class scope'

  def fun()  p CONSTANT  end
end
C.new.fun    # prints: "class scope"


# monkey-patching with #class_eval: uses surrounding scope!
C.class_eval do
  def fun()  p CONSTANT  end
end
C.new.fun    # prints: "surrounding scope"


# monkey-patching by re-opening the class: uses scope of class C
class C
  def fun()  p CONSTANT  end
end
C.new.fun    # prints: "class scope"

Похожие вопросы