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

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


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

использовать

51

# метод Object#extend: a.extend(Greeting)
class []
# => "Hi!"

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

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

Возможность расширения классов и объектов в Ruby вызывает много споров. Кто-то справедливо
считает, что необдуманное ее применение приведет к проблемам, особенно при сопровождении
больших проектов. Тем не менее, эта возможность в Ruby есть. Выделяются, как минимум, две
ситуации, в которых она будет востребована:




смена реализации методов без останова приложения. Это важно для приложений,
работающих в режиме 24x7, поскольку можно исправлять небольшие ошибки без
перезапуска приложений.
Наполнение
специфической прикладной функциональностью
чужих классов
без
модификации их исходных текстов (как делает Ruby-On-Rails со стандартными классами
Integer, Time и String).

Как бы то ни было, динамическое расширение классов, модулей и объектов является одной из
основополагающих возможностей языка. Поэтому при использовании Ruby нужно принимать ее во
внимание, поскольку она действительно предоставляет уникальные возможности.

4.17 Переопределение методов
Как было показано выше, в Ruby можно переопределить метод объекта. Но не всегда требуется
полностью менять реализацию метода, иногда необходимо дополнить уже существующий метод
дополнительными действиями. Например, захватить какой-то ресурс в эксклюзивном режиме,
выполнить действия метода, после чего освободить ресурс. В Ruby это выполняется посредством
назначения псевдонима, осуществляемого с помощью Method#alias_method, с последующим
переопределением метода:

class Demo
def do_something
puts ’doing something’
end
end
d = Demo.new
# Используется оригинальная версия метода do_something.
d.do_something
# Теперь объект d расширяется методами lock_resource, unlock_resource
# и получает новую реализацию метода do_something.
class nil
# => "a"
# => :BlaBlaBla

Показанный выше класс ValueHolder является примитивной демонстрацией принципа работы класса
OpenStruct [37] из стандартной библиотеки Ruby, в основе которого также лежит использование
method_missing.
Но наибольшее значение method_missing имеет для построения DSL – настолько важное, что даже в
документации к Kernel#method_missing приводится пример класса для хранения римских чисел, в
котором method_missing служит для разбора римской нотации:

class Roman
def romanToInt(str)
# ...
end
def method_missing(methId)
str = methId.id2name
romanToInt(str)
end
end
r = Roman.new
r.iv
r.xxiii
r.mm

#=> 4
#=> 23
#=> 2000

Одним из самых ярких и впечатляющих примеров использования method_missing для организации
DSL является библиотека Builder [38], которая позволяет записывать XML/HTML-конструкции в виде
Ruby-кода:

require ’rubygems’
require_gem ’builder’, ’~> 2.0’
builder = Builder::XmlMarkup.new
xml = builder.person { |b|
b.name
"Jim"
b.phone
"555-1234"
b.address { |a|
a.town
"New-York"
a.zip
"655374"
a.street "Broadway"
}
}
xml # => "Jim555-1234
New-York655374
Broadway"

55

4.19 Утиная типизация
Еще одной, очень горячо обсуждаемой особенностью Ruby является т.н. «утиная типизация» (duck
typing). Ее принцип состоит в том, что если некий объект «ходит как утка и крякает как утка»
значит он — утка. Подобие объекта чему-либо проверяется наличием методов с нужными именами и
сигнатурами, то есть наличие у объекта подходящего метода более важно, чем наследование класса
объекта от некоторого базового класса.
Например, пусть требуется создать метод do_something_important, который протоколирует ход
своей работы в журнальный файл. Объект файла-журнала должен передаваться методу в качестве
параметра. Поскольку задача do_something_important заключается в выполнении собственных
действий, то подготовка подходящих для этого условий – не его проблема:

def do_someting_important(log)
log