Приблизительно в 19:00 отмечают в его презентации RailsConf, переговорах David Heinemeier Hansson об оборотных сторонах instance_eval
:
В течение долгого времени я разглагольствовал и бредил против
instance_eval
, который является понятием не использования параметра, к которому приводят (какdo |people|
) и просто прямоdo something
и затем оцените то, что находится в том блоке в рамках того, куда Вы произошли из (я даже не знаю, является ли это когерентным объяснением),В течение долгого времени мне не нравилось это, потому что это чувствовало себя более сложным в некотором смысле. Если Вы хотели поместить свой собственный код, там были Вы собирающийся инициировать что-то, что уже было там? Вы собирались переопределить что-то? При получении определенной переменной, можно объединить все в цепочку от этого, и можно знать [Вы] не смешивающий ни с чьим больше материалом
Это звучало интересным, но a) я не знаю как как instance_eval
работы во-первых и b), который я не понимаю, почему это может быть плохо / сложность увеличения.
Кто-то может объяснить?
Что делает 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. Таким образом, его действительно следует использовать только в тех случаях, когда вероятность того, что это может понадобиться, очень мала.
Идея в том, что это немного опасно, потому что вы никогда не можете быть уверены, что не собираетесь что-то сломать, не прочитав весь код, который имеет дело с объектом, для которого вы используете instance_eval.
Также, если вы, скажем, обновили библиотеку, которая не сильно изменила интерфейс, но изменила множество внутренних компонентов объекта, вы действительно могли бы нанести некоторый ущерб.
Итак, идея здесь в том, что вместо чего-то вроде этого
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. перед вашими методами?