Программирование на языке Ruby
Программирование на языке Ruby читать книгу онлайн
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
11.3.3. Динамическое создание экземпляра класса, заданного своим именем
Такой вопрос мы видели многократно. Пусть дана строка, содержащая имя класса; как можно создать экземпляр этого класса?
Правильный способ — воспользоваться методом
const_get
Object
classname = "Array"
klass = Object.const_get(classname)
x = klass.new(4, 1) # [1, 1, 1, 1]
А если имена вложены? Как выясняется, следующий код не работает:
class Alpha
class Beta
class Gamma
FOOBAR =237
end
end
end
str = "Alpha::Beta::Gamma::FOOBAR"
val = Object.const_get(str) # Ошибка!
Дело в том, что метод
const_get
# Структура класса та же
str = "Alpha::Beta::Gamma::FOOBAR"
val = str.split("::").inject(Object) {|x,y| x.const_get(y) } # 237
Такой код встречается часто (и демонстрирует интересное применение
inject
11.3.4. Получение и установка переменных экземпляра
Отвечая на пожелание употреблять
eval
class MyClass
attr_reader :alpha, :beta
def initialize(a,b,g)
@alpha, @beta, @gamma = a, b, g
end
end
x = MyClass.new(10,11,12)
x.instance_variable_set("@alpha",234)
p x.alpha # 234
x.instance_variable_set("@gamma",345) # 345
v = x.instance_variable_get("@gamma") # 345
Прежде всего, отметим, что имя переменной должно начинаться со знака
@
attr_accessor
@
Не нарушает ли существование таких методов принцип инкапсуляции? Нет. Конечно, эти методы потенциально опасны. Пользоваться ими следует с осторожностью, а не при всяком удобном случае. Но нельзя говорить, что инкапсуляция нарушена, не видя, как эти инструменты применяются в конкретном случае. Если это делается обдуманно, ради ясно осознанной цели, то все хорошо. Если же цель состоит в том, чтобы нарушить проект или обойти неудачное проектное решение, это печально. Ruby намеренно предоставляет доступ к внутренним деталям объектов тем, кому это действительно нужно; ответственный программист не станет пользоваться свободой во вред.
11.3.5. Метод define_method
Помимо ключевого слова
def
define_method
Конечно, в Ruby практически все происходит во время выполнения. Если окружить определение метода обращениями к
puts
class MyClass
puts "до"
def meth
#...
end
puts "после"
end
Но внутри тела метода или в другом аналогичном месте нельзя заново открыть класс (если только это не синглетный класс). В таком случае в прежних версиях Ruby приходилось прибегать к помощи
eval
define_method
Первая (ошибочная) попытка воспользоваться этим методом могла бы выглядеть так:
# Не работает, так как метод define_method закрытый.
if today =~ /Saturday | Sunday/
define_method(:activity) { puts "Отдыхаем!" }
else
define_method(:activity) { puts "Работаем!" }
end
activity
Поскольку
define_method
# Работает (Object - это контекст верхнего уровня).
if today =~ /Saturday | Sunday/
Object.class_eval { define_method(:activity) { puts "Отдыхаем!" } }
else
Object.class_eval { define_method(:activity) { puts "Работаем!" } }
end
activity
Можно было бы поступить так же внутри определения класса (в применении к классу
Object
class MyClass
define_method(:mymeth) { puts "Это мой метод." }
end
Есть еще один трюк: включить в класс метод, который сам вызывает
define_method