Ruby - новые грани [Евгений Охотников] (pdf) читать постранично, страница - 18

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

где-то.
class ProjectDescription
...
def presentations; ...; end
end
Такое расширение уже существующих классов может оказаться востребованным в различных
ситуациях. Например, когда реализация части методов зависит от платформы, на которой работает
программа:

47

class Application
def run
load_configuration
perform_work
store_results
end
def perform_work; ...; end
def store_results; ...; end
end
# Реализация метода load_configuration зависит от платформы.
if /mswin/ =~ Config::CONFIG[ ’host_os’ ]
require ’app-mswin’
else
require ’app-non-mswin’
end
# Файл app-mswin.rb. Реализация загрузки конфигурации из реестра.
class Application
def load_configuration; ...; end
end
# Файл app-non-mswin.rb. Реализация загрузки конфигурации из файлов.
class Application
def load_configuration; ...; end
end
При расширении класса автоматически расширяются все уже созданные объекты этого класса. То
есть, если в класс были добавлены новые методы, они автоматически появляются в каждом из уже
существующих объектов:

class Demo
def first; end
def second; end
end
a = Demo.new
a.class.instance_methods(false)

# => ["second", "first"]

class Demo
def third; end
end
a.class.instance_methods(false)

# => ["second", "first", "third"]

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

class Demo
def greeting
’hi’
end
end
a = Demo.new
a.greeting

# => "hi"

class Demo
def greeting
’Hello’
end
end
a.greeting

# => "Hello"

48

Расширять в Ruby можно любые классы, даже стандартные. Например, Ruby-On-Rails расширяет
стандартные классы Integer, String и Time для предоставления удобных вспомогательных
методов [19]:

puts
puts
puts
puts
puts

20.kilobytes
20.hours.from_now
"cat".pluralize
"cats".singularize
Time.now.at_beginning_of_month

#
#
#
#
#

=>
=>
=>
=>
=>

20480
Wed May 11 13:03:43 CDT 2005
cats
cat
Sun May 01 00:00:00 CDT 2005

Все вышесказанное распространяется и на модули. Модули, как и классы, являются открытыми.
Поэтому расширение модуля или смена реализации метода в модуле автоматически
распространяется на объекты всех типов, в которых модуль был использован в качестве примеси:

module Greeting
def hello; ’Hi!’; end
end
class Demo
include Greeting
end
a = Demo.new
a.public_methods.grep(/(hello|applaud)/)
a.hello

# => ["hello"]
# => "Hi!"

# Теперь модуль Greeting модифицируется.
module Greeting
def hello; ’Hello!’; end
def applaud; ’Bravo!’; end
end
a.public_methods.grep(/(hello|applaud)/)
a.hello
a.applaud

# => ["hello", "applaud"]
# => "Hello!"
# => "Bravo!"

(метод public_methods по умолчанию возвращает список всех публичных методов объекта, включая
унаследованные из базовых классов и примесей, но этот список большой, поэтому из него
выделяются только методы с именами hello и applaud).
То, что в Ruby можно снабдить собственными методами любой, даже чужой, класс, открывает перед
разработчиком новые возможности. Например, если при использовании сторонней библиотеки
обнаруживается ошибка в ее реализации, то эта ошибка может быть исправлена без модификации
исходного текста библиотеки — достаточно модифицировать проблемный класс в своем коде. Еще
один пример: при использовании какого-то готового фреймворка может потребоваться
модифицировать какой-либо его класс. Скажем, для того, чтобы снабдить его новой
функциональностью или дополнительными атрибутами. Сделать это при помощи обычного
наследования невозможно, если объекты нужного класса создаются где-то внутри фреймворка. Но
можно расширить класс собственными методами/атрибутами, и тогда фреймворк автоматически
начнет создавать объекты уже обновленного класса. Ruby-On-Rails демонстрирует это на примере
стандартного класса Integer, объекты которого создаются где-то в глубине интерпретатора Ruby.
Открытыми в Ruby являются не только классы, но и объекты. Любой объект может быть расширен
собственными методами, которые будут присутствовать только у него, но не у других объектов того
же класса:

class
def
def
def
end

Demo
f; end
g; end
j; end

49

a, b, c = Demo.new, Demo.new, Demo.new
a.public_methods(false)
# => ["g", "f", "j"]
b.public_methods(false)
# => ["g", "f", "j"]
c.public_methods(false)
# => ["g", "f", "j"]
# Добавление нового метода в объект a.
def a.k
end
# Добавление нового метода в объект b. Используется другой способ записи.
class ["g", "f", "j", "m"]
# => ["g", "f", "j"]

(обращение к методу public_methods с аргументом false приводит к возврату списка только
собственных методов объекта, без унаследованных из базовых классов и примесей).
Можно не только расширять объект новыми методами, но и менять реализацию существующих
методов. Причем делать это не только для собственных объектов, но и для любых объектов, в том
числе и для объектов стандартных типов:

a = [ 1, ’1’, :first ]
b = a.dup
a.to_s
b.to_s

# => "11first"
# => "11first"

class "11first"

Cпособ записи расширения объекта в виде конструкции class 20943200
class []

# Выполняем подмешивание модуля Greeting только к объекту a.
# Как вариант более компактной записи можно было бы