Войти на сайт

Авторизация, ждите ...
×

ТЕМА: Удобная работа с With

Удобная работа с With 9 года 6 мес. назад #73485

  • DeadElf79
  • DeadElf79 аватар
  • Вне сайта
  • Звездный Страж
  • Сообщений: 3147
  • Спасибо получено: 2650
  • ВетеранПрограммист RubyПроект месяца 1 местоУчитель3 место1 место в ГотвПисатель 3 местоОрганизатор конкурсовПроект месяца 2 место
Этот метод мне сильно понравился еще в будни использования VisualBasic, а позднее - и в Delphi. Сегодня я хочу показать его и вам.

Как это работает?
Допустим, у вас есть какой-нибудь объект, содержащий несколько методов, которые вам нужно вызывать последовательно, чтобы добиться нужного результата. Чаще всего это можно заметить в отрисовке в различных меню и вот как оно выглядит:
def refresh
	self.contents.clear
	self.contents.font.color=system_color
	self.contents.font.bold=true
	self.contents.draw_text(0,0,width-32,32,'Some Text')
end

Что вы можете сделать, чтобы не переписывать один и тот же кусок текста (self.contents)?
  1. Скопировать и вставлять с помощью Ctrl+C и Ctrl+V
  2. Использовать With

Так выглядит код во втором случае:
def refresh
	with (self.contents) {
		clear
		font.color=system_color
		font.bold=true
		draw_text(0,0,width-32,32,'Some Text')
	}
end

Неправда ли, куда проще?

Описание with
Так выглядит сам метод with, который добавляется прямо в Kernel:
def with(instance, &block)
  instance.instance_eval(&block)
  instance
end

Он получает переменную и выполняет в ее контексте указанные в блоке действия, после чего возвращает саму переменную. В качестве переменной также может быть вызов конструктора какого-либо класса. Пример:
def with(instance, &block)
  instance.instance_eval(&block)
  instance
end
 
class Abc
	def some
		'some'
	end
 
	def somedo
		'moar some'
	end
end
 
name=Abc.new
puts name.class
with (name) {
	puts some
	puts somedo
}
new_name=with (Abc.new) { puts some }
puts new_name.class

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

Более сложный пример для самых навороченных (или ленивых?):
def with(instance, &block)
  instance.instance_eval(&block)
  instance
end
 
class Abc
	attr_reader :defg
	def initialize
		@defg=Defg.new
	end
 
	def some
		'some'
	end
 
	def somedo
		'moar some'
	end
 
	class Defg
		def moar_work
			'i\'m feelin\' lucky!'
		end
	end
end
 
name=Abc.new
with (name) {
	with(defg) {
		puts moar_work
	}
}

В данном случае мы видим пример того, что в отрисовке можно при желании можно в такой же блок метода with вписать помимо self.contents еще и font. Как-то так:
def refresh
	with (self.contents) {
		clear
		with(font) {
			color=system_color
			bold=true
		}
		draw_text(0,0,width-32,32,'Some Text')
	}
end

Оригинал кода взят из ответа на данный вопрос: #with(object) &block trick (StackOverflow.com)
Последнее редактирование: 9 года 6 мес. назад от DeadElf79.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Lekste, Ren310, strelokhalfer, Amphilohiy, yuryol, KageDesu

Удобная работа с With 9 года 6 мес. назад #73486

  • DeadElf79
  • DeadElf79 аватар
  • Вне сайта
  • Звездный Страж
  • Сообщений: 3147
  • Спасибо получено: 2650
  • ВетеранПрограммист RubyПроект месяца 1 местоУчитель3 место1 место в ГотвПисатель 3 местоОрганизатор конкурсовПроект месяца 2 место
Обнаружил небольшую ошибку во втором (расширенном) примере, по причине возникновения которой не рекомендую все же использовать несколько with один в другом.
def refresh
	with (self.contents) {
		clear
		with(font) {
			color=system_color
			bold=true
		} # по окончании блока изменения могут никак не отразиться на шрифте
		draw_text(0,0,width-32,32,'Some Text')
	}
end

Лучше использовать:
def refresh
		with (self.contents) {
			clear
			font.color=Color.new(255,0,0)
			font.bold=true
			draw_text(0,0,width-32,32,'Some Text')
		}
end

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

Также, обнаружилось дополнительное правило, вполне логичное, но незамеченное мною изначально:
Все выполняется в контексте указанного объекта. Соответственно, использовать метод system_color из класса Window_Base, содержащего Bitmap (в contents) уже нельзя. Если придумаю как можно красиво обойти это - дополню тему.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: strelokhalfer

Удобная работа с With 9 года 2 нед. назад #78773

  • strelokhalfer
  • strelokhalfer аватар
  • Вне сайта
  • Архитектор Миров
  • Знатный грамотей
  • Сообщений: 1640
  • Спасибо получено: 1078
  • Даритель Стимкея2 место 2 место Сбитая кодировкаПереводчикПрограммист RubyОрганизатор конкурсов
Перенес в Факультет, думаю, там оно нужнее.
"Стрелок, что-то ты неочень похож на свой аватар..."(с)
Администратор запретил публиковать записи гостям.
Время создания страницы: 0.235 секунд