Как instance_eval работает и почему DHH ненавидит его?

Приблизительно в 19:00 отмечают в его презентации RailsConf, переговорах David Heinemeier Hansson об оборотных сторонах instance_eval:

В течение долгого времени я разглагольствовал и бредил против instance_eval, который является понятием не использования параметра, к которому приводят (как do |people|) и просто прямо do something и затем оцените то, что находится в том блоке в рамках того, куда Вы произошли из (я даже не знаю, является ли это когерентным объяснением),

В течение долгого времени мне не нравилось это, потому что это чувствовало себя более сложным в некотором смысле. Если Вы хотели поместить свой собственный код, там были Вы собирающийся инициировать что-то, что уже было там? Вы собирались переопределить что-то? При получении определенной переменной, можно объединить все в цепочку от этого, и можно знать [Вы] не смешивающий ни с чьим больше материалом

Это звучало интересным, но a) я не знаю как как instance_eval работы во-первых и b), который я не понимаю, почему это может быть плохо / сложность увеличения.

Кто-то может объяснить?

21
задан Tom Lehman 18 June 2010 в 16:43
поделиться

3 ответа

Что делает instance_eval , так это то, что он запускает блок в контексте другого экземпляра. Другими словами, он изменяет значение self , что означает, что он меняет значение методов экземпляра и переменных экземпляра.

Это создает когнитивное разъединение: контекст, в котором выполняется блок, не является контекстом, в котором он появляется на экране.

Позвольте мне продемонстрировать это с небольшой вариацией примера @Matt Briggs. Допустим, мы создаем электронное письмо вместо формы:

def mail
  builder = MailBuilder.new
  yield builder
  # executed after the block 
  # do stuff with builder 
end

mail do |f|
  f.subject @subject
  f.name    name
end

В этом случае @subject - это переменная экземпляра вашего объекта , а имя - метод вашего класса . Вы можете использовать красивую объектно-ориентированную декомпозицию и сохранить тему в переменной.

def mail &block
  builder = MailBuilder.new
  builder.instance_eval &block
  # do stuff with builder 
end

mail do 
  subject @subject
  name    name # Huh?!?
end

В этом случае @subject является переменной экземпляра объекта mail builder ! Возможно, его даже не существует ! (Или, что еще хуже, он может существовать и содержать какое-то совершенно глупое значение.) У вас нет способа получить доступ к переменным экземпляра вашего объекта. И как вы даже вызываете метод name вашего объекта? Каждый раз, когда вы пытаетесь вызвать его, вы получаете метод конструктора почты .

По сути, instance_eval затрудняет использование вашего собственного кода внутри кода DSL. Таким образом, его действительно следует использовать только в тех случаях, когда вероятность того, что это может понадобиться, очень мала.

31
ответ дан 29 November 2019 в 20:09
поделиться

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

Также, если вы, скажем, обновили библиотеку, которая не сильно изменила интерфейс, но изменила множество внутренних компонентов объекта, вы действительно могли бы нанести некоторый ущерб.

0
ответ дан 29 November 2019 в 20:09
поделиться

Итак, идея здесь в том, что вместо чего-то вроде этого

form_for @obj do |f|
  f.text_field :field
end

вы получаете что-то вроде этого

form_for @obj do 
  text_field :field
end

первый способ довольно прост, вы получаете шаблон это выглядит так

def form_for
  b = FormBuilder.new
  yield b
  b.fields.each |f|
    # do stuff
  end
end

, вы передаете объект построителя, для которого потребитель вызывает методы, а затем вызываете методы объекта построителя, чтобы фактически построить форму (или что-то еще)

, второй вариант немного более волшебный

def form_for &block
  b = FormBuilder.new
  b.instance_eval &block
  b.fields.each |f|
    #do stuff
  end
end

в этом примере вместо того, чтобы передавать конструктор блоку, мы берем блок и оцениваем его в контексте построителя

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

17
ответ дан 29 November 2019 в 20:09
поделиться
Другие вопросы по тегам:

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