Чтение онлайн

ЖАНРЫ

Программирование на языке Ruby
Шрифт:

Но в Ruby есть минимальная поддержка ФП, он располагает богатым набором методов для манипулирования массивами (списками) и поддерживает объекты

Proc
, позволяющие инкапсулировать и многократно вызывать код. Ruby также допускает сцепление методов, весьма распространенное в ФП. Правда, дело портят «восклицательные» методы (например,
sort!
или
gsub!
), которые возвращают
nil
, если вызывающий объект не изменился в результате выполнения.

Предпринимались попытки создать библиотеку, которая стала бы «уровнем совместимости» с ФП, заимствуя некоторые идеи из языка Haskell. Пока эти попытки ни к чему завершенному не привели.

Интересна идея аспектно-ориентированного

программирования (АОП). Это попытка рассечь модульную структуру программы. Иными словами, некоторые задачи и механизмы системы разбросаны по разным участкам кода, а не собраны в одном месте. То есть мы пытаемся придать модульность вещам, которым в традиционном объектно-ориентированном или процедурном программировании с трудом поддаются «модуляризации». Взгляд на программу оказывается перпендикулярен обычному.

Разумеется, Ruby создавался без учета АОП. Но это гибкий и динамический язык, поэтому не исключено, что такой подход может быть реализован в виде библиотеки. Уже сейчас существует библиотека AspectR, представляющая собой первую попытку внести аспектно-ориентированные черты в Ruby; последнюю ее версию можно найти в архиве приложений Ruby.

Идея «проектирования по контракту» (Design by Contract — DBC) хороша знакома поклонникам языка Eiffel, хотя и вне этого круга она тоже известна. Смысл состоит в том, что некоторый кусок кода (метод или класс) реализует контракт; чтобы код правильно работал, должны выполняться определенные предусловия, и тогда гарантируется, что по завершении работы будут выполнены некоторые постусловия. Надежность системы можно существенно повысить, введя возможность формулировать контракт явно и автоматически проверять его во время выполнения. Полезность такого подхода подкрепляется наследованием информации о контракте при расширении классов.

В язык Eiffel методология DBC встроена явно, в Ruby — нет. Однако имеется по крайней мере две работающие библиотеки, реализующие DBC, и мы рекомендуем вам выбрать одну из них и изучить внимательнее.

Паттерны проектирования стали темой оживленных дискуссий на протяжении последних нескольких лет. Конечно, они мало зависят от конкретного языка и могут быть реализованы на самых разных языках. Но необычайная гибкость Ruby, возможно, делает их практически более полезными, чем в других средах. Хорошо известные примеры приведены в других местах; паттерн Visitor (Посетитель) реализуется стандартным итератором

each
, а многие другие паттерны входят в стандартный дистрибутив Ruby (библиотеки
delegator.rb
и
singleton.rb
).

С каждым днем все больше приверженцев завоевывает методология экстремального программирования (Extreme Programming — XP), поощряющая, среди прочего, раннее тестирование и постоянную переработку (рефакторинг).

XP — технология, не зависящая от языка, хотя к некоторым языкам она, возможно, более приспособлена. Разумеется, на наш взгляд, в Ruby рефакторинг реализуется проще, чем во многих языках, но это субъективное мнение. Однако, наличие библиотеки

Test::Unit
(и других) позволяет «поженить» Ruby и XP. Эта библиотека облегчает автономное тестирование компонентов, она функциональна богата, проста в использовании и доказала свою полезность в ходе разработки эксплуатируемых в настоящее время программ на Ruby. Мы горячо поддерживаем рекомендуемое XP раннее и частое тестирование, а тем, кто желает воплотить этот совет в Ruby, предлагаем ознакомиться с
Test::Unit
. (
ZenTest
— еще один отличный пакет, включающий некоторые возможности, которые в
Test::Unit
отсутствуют.)

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

Конференция comp.lang.ruby

Архив приложений Ruby

rubyforge.org

ruby-doc.org

Есть и другие полезные ресурсы, особенно для тех, кто говорит по-японски.

Трудно перечислять онлайновые ресурсы в печатном издании, поскольку они постоянно изменяются. Поисковая машина — ваш лучший друг.

11.3. Динамические механизмы

Скайнет осознал себя в 2:14 утра по восточному времени 29 августа 1997 года.

Терминатор 2, Судный День

Многие читатели имеют опыт работы со статическими языками, например С. Им я адресую риторический вопрос: «Можете ли вы представите себе написанную на С функцию, которая принимает строку, рассматривает ее как имя переменной и возвращает значение этой переменной?»

Нет? А как насчет того, чтобы удалить или заменить определение функции? А перехватить обращения к несуществующим функциям? Или узнать имя вызывающей функции? Или автоматически получить список определенных пользователем элементов программы (например, перечень всех написанных вами функций)?

В Ruby все это возможно. Такая гибкость во время выполнения, способность опрашивать и изменять программные элементы во время выполнения намного упрощают решение задач. Утилиту трассировки выполнения, отладчик, профилировщик — все это легко написать на Ruby и для Ruby. Хорошо известные программы

irb
и
xmp
, используя динамические возможности Ruby, творят это волшебство.

К подобным возможностям нужно привыкнуть, их легко употребить во вред. Все эти идеи появились отнюдь не вчера (они стары по крайней мере так же, как язык LISP) и считаются «проверенными и доказанными» в сообществах пользователей Scheme и Smalltalk. Даже в языке Java, который так многим обязан С и C++, есть некоторые динамические средства, поэтому мы ожидаем, что со временем их популярность будет только расти.

11.3.1. Динамическая интерпретация кода

Глобальная функция

eval
компилирует и исполняет строку, содержащую код на Ruby. Это очень мощный (и вместе с тем опасный) механизм, поскольку позволяет строить подлежащий исполнению код во время работы программы. Например, в следующем фрагменте считываются строки вида «имя = выражение», затем каждое выражение вычисляется, а результат сохраняется в хэше, индексированном именем переменной.

parameters = {}

ARGF.each do |line|

 name, expr = line.split(/\s*=\s*/, 2)

 parameters[name] = eval expr

end

Пусть на вход подаются следующие строки:

а = 1

b = 2 + 3

с = 'date'

Тогда в результате мы получим такой хэш:

{"а"=>1, "b"=>5,"с"=>"Mon Apr 30 21:17:47 CDT 2001\n"}
. На этом примере демонстрируется также опасность вычисления с помощью
eval
строк, содержимое которых вы не контролируете; злонамеренный пользователь может подсунуть строку
d= 'rm *'
и стереть всю вашу дневную работу.

В Ruby есть еще три метода, которые интерпретируют код «на лету»:

class_eval
,
module_eval
и
instance_eval
. Первые два — синонимы, и все они выполняют одно и то же: интерпретируют строку или блок, но при этом изменяют значение псевдопеременной
self
так, что она указывает на объект, от имени которого эти методы вызваны. Наверное, чаще всего метод
class_eval
применяется для добавления методов в класс, на который у вас имеется только ссылка. Мы продемонстрируем это в коде метода
hook_method
в примере утилиты
Trace
в разделе 11.3.13. Другие примеры вы найдете в динамических библиотечных модулях, например
delegate.rb
.

Поделиться с друзьями: