27

Метапрограммирование

Никакая гениальность не спасает от любви к подробностям.

Восьмой закон Леви

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

Поэтому мы говорим: "Долой подробности!". Уберите их из программы. В этом случае мы можем сделать нашу программу гибкой при настройке и легко адаптирующейся к изменениям.

Динамическая конфигурация

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

Подсказка 37: Осуществляйте настройку, а не интеграцию

Используйте метаданные для спецификации вариантов настройки приложения: подгонки параметров, глобальных параметров пользователя, каталога, в который производится установка приложения, и т. д.

Так что же такое метаданные? Строго говоря, метаданные – это данные о данных. Наиболее распространенным примером, вероятно, является схема базы данных или словарь данных. Схема содержит данные, которые описывают поля (столбцы) в терминах имен, длины и других атрибутов. Вы должны иметь возможность доступа к этой информации и ее обработки так, как если бы это были любые другие данные в этой базе.

Мы используем этот термин в самом широком смысле. Метаданные – это любые данные, которые описывают приложение – как оно выполняется, какие ресурсы обязано использовать и т. д. Обычно доступ к данным и их использование осуществляется на этапе выполнения, а не компиляции. Вы используете метаданные все время, по крайней мере, это делают ваши программы. Предположим, вы щелкаете мышью для того, чтобы скрыть панель инструментов в интернет-браузере. Браузер будет сохранять эти глобальные параметры как метаданные в своего рода внутренней базе данных.

Эта база данных может быть сформирована в собственном формате или может воспользоваться стандартным механизмом. При работе в операционной системе Windows таким механизмом является либо файл инициализации (используется суффикс .ini), либо записи в системном реестре. При работе с Unix подобная функциональная возможность обеспечивается системой X Window с помощью файлов Application Default. Java использует файлы Property. Во всех этих средах для извлечения значения вы указываете ключ. В других, более мощных и гибких реализациях метаданных используется встроенный язык сценариев (см. "Языки, отражающие специфику предметной области").

При реализации этих глобальных параметров в браузере Netscape фактически использованы обе эти технологии. В версии 3 параметры сохранялись в виде пар "ключ-значение":

SHOWTOOLBAR: False

В версии 4 параметры больше напоминали JavaScript:

user_pref("custtoolbar.Browser.Navigation_Toolbar.open", false);

Приложения, управляемые метаданными

Но мы хотим большего, нежели использовать метаданные для простых глобальных параметров. Мы хотим настраивать и управлять приложением через метаданные – насколько это возможно. Наша цель – думать описательно (обозначая, что должно быть сделано, а не как это должно быть сделано) и создавать высокодинамичные и адаптируемые программы. Это можно сделать, придерживаясь общего правила: программировать для общего случая и помещать всю специфику в другое место – за пределы компилируемого ядра программы.

Подсказка 38: Помещайте абстракции в текст программы, а подробности – в область метаданных

Этот подход характеризуется несколькими преимуществами:

• Он вынуждает вас делать конструкцию несвязанной, что приводит к созданию более гибкой и адаптируемой программы.

• Он заставляет вас создавать более устойчивую, абстрактную конструкцию за счет отнесения подробностей, выводя все подробности за пределы программы.

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

• Метаданные могут быть выражены способом, который находится намного ближе к предметной области, по сравнению с универсальным языком программирования (см. "Языки, отражающие специфику конкретной области").

• Вы даже сможете реализовывать несколько различных проектов, используя то же самое ядро приложения, но с различными метаданными.

Как правило, нам хочется отложить определение большинства подробностей на последний момент и оставить их как можно менее сложными для изменения. Создавая решение, позволяющее нам вносить изменения быстро, мы можем лучше справляться с потоком направленных сдвигов, которые погубили многие проекты (см. "Обратимость").

Бизнес-логика

Итак, мы выбрали механизм базы данных в качестве опции настройки и предусмотрели метаданные для определения стиля пользовательского интерфейса. Можем ли мы сделать большее? Несомненно.

Поскольку стратегия и бизнес-правила подвергнутся изменениям скорее, нежели любые другие аспекты проекта, есть смысл поддерживать их в очень гибком формате.

Например, приложение, автоматизирующее процесс закупок, может включать в себя различные корпоративные стратегии. Может быть, вы производите оплату небольшим фирмам-поставщикам через 45 дней, а большим – через 90 дней. Сделайте настраиваемыми определения типов поставщиков, а также самих периодов времени. Используйте возможность обобщения.

Возможно, вы создаете систему с ужасающими требованиями к последовательности операций. Действия начинаются и заканчиваются согласно сложным (и изменяющимся) бизнес-правилам. Подумайте об их реализации в виде некой системы на основе правил (или экспертной системы), встроенных в ваше приложение. Тем самым вы осуществите его настройку за счет написания правил, а не программы.

Менее сложная логика может быть выражена при помощи мини-языка, что делает необязательным повторную компиляцию и развертывание при изменении среды. Пример приведен в разделе "Языки, отражающие специфику предметной области".

Когда осуществлять настройку

Как было упомянуто в разделе "Преимущество простого текста", рекомендуется представлять метаданные о настройке в формате простого текста – это делает жизнь проще.

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

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

Данное явление не ограничивается прикладными программами. Все мы раздражаемся, если операционные системы заставляют нас проводить перезагрузку при установке простых приложений или изменении совершенно безвредного параметра.