о = MyClass.new
puts o.a # 123
puts o.b # Mon Apr 30 23:12:15 CDT 2001
Вы наверняка сможете придумать и другие, более изобретательные применения. Метод extend
подмешивает к объекту модуль. Методы экземпляра, определенные в модуле, становятся методами экземпляра объекта. Взгляните на листинг 11.11.
module Quantifier
def any?
self.each { |x| return true if yield x }
false
end
def all?
self.each { |x| return false if not yield x }
true
end
end
list = [1, 2, 3, 4, 5]
list.extend(Quantifier)
flag1 = list.any? {|x| x > 5 } # false
flag2 = list.any? {|x| x >= 5 } # true
flag3 = list.all? {|x| x <= 10 } # true
flag4 = list.all? {|x| x % 2 == 0 } # false
В этом примере к массиву list
подмешаны методы any?
и all?
.
11.2.3. Вложенные классы и модули
Классы и модули можно вкладывать друг в друга произвольным образом. Программисты, приступающие к изучению Ruby, могут этого и не знать.
Основная цель данного механизма — упростить управление пространствами имен. Скажем, в класс File
вложен класс Stat
. Это помогает «инкапсулировать» класс Stat
внутри тесно связанного с ним класса, а заодно оставляет возможность в будущем определить класс Stat
, не конфликтуя с существующим (скажем, для сбора статистики).
Другой пример дает класс Struct::Tms
. Любая новая структура Struct
помещается в это пространство имен, не «загрязняя» расположенные выше, a Tms
— в действительности тоже Struct
.
Кроме того, вложенный класс можно создавать просто потому, что внешний мир не должен знать о нем или обращаться к нему. Иными словами, можно создавать целые классы, подчиняющиеся тому же принципу «сокрытия данных», которому переменные и методы экземпляра следуют на более низком уровне.
class BugTrackingSystem
class Bug
#...
end
#...
end
# Никто снаружи не знает о классе Bug.
Можно вкладывать класс в модуль, модуль в класс и т.д. Если вы придумаете интересные и изобретательные способы применения этой техники, дайте нам знать.
11.2.4. Создание параметрических классов
Изучи правила, потом нарушай их.
Предположим, что нужно создать несколько классов, отличающихся только начальными значениями переменных уровня класса. Напомним, что переменная класса обычно инициализируется в самом определении класса.
class Terran
@@home_planet = 'Earth'
def Terran.home_planet
@@home_planet
end
def Terran.home_planet= (x)
@@home_planet = x
end
#...
end
Все замечательно, но что если нам нужно определить несколько подобных классов? Новичок подумает: «Ну так я просто определю суперкласс!» (листинг 11.12).
class IntelligentLife # Неправильный способ решения задачи!
@@home_planet = nil
def IntelligentLife.home_planet
@@home _planet
end