WWW.LIBRUS.DOBROTA.BIZ
БЕСПЛАТНАЯ  ИНТЕРНЕТ  БИБЛИОТЕКА - собрание публикаций
 

Pages:     | 1 || 3 | 4 |   ...   | 5 |

«Мартина ФаУflера Предметно-ориентированное П оекти ОБ иие еТРУКТУРИ3АЦИЯ елО жныIx ПРО rPAMMHbIX СИ еТЕМ Domain -Driven Design TACKLING COMPLEXITY IN ТНЕ HEART OFSOFTWARE Eric Evans Addison-Wesley ...»

-- [ Страница 2 ] --

Многие СЛУЖБЫ уровней модели и прикладных операций строятся на основе сово­ купностей ОБЪЕКТОВ-СУЩIIОСТЕЙ и ОБЪЕКТОВ-ЗНАЧЕНИЙ. Они ведут себя как сцена­ рии, которые моБИЛИЗУIОТ потенциал предметной области на выполнение чего-то полез­ дать пользователю удобный доступ к возможностям уровня предметной области. Можно ного. Сами СУЩНОСТИ и ЗНАЧЕI-IИЯ часто бывают слишком мелкомасштабными, чтобы заметить, что грань между уровнями предметной области и прикладных операций очень тонка. Например, если банковская программа умеет преобразовывать и экспортировать все наПIИ финансовые транзакции в файл электронной таблицы (чтобы мы могли его чи­ тать и анализировать), то СЛУЖБА такого экспорта относится к уровню прикладных опе­ раций. В предметной области банковского дела никаких "форматов файлов" нет, да и ло­ гика прикладной модели здесь тоже не используется .

С другой стороны, функция перевода денег с одного счета на другой - это СЛУЖБА предметной области, потому что в ней реализуются ее алгоритмы, деловые регламенты (например, ведение дебета и кредита по счетам), а также потому, что "перевод денежных средств" - это термин банковского дела. В этом случае сама СЛУЖБА не делает ничего 1-10 поместить операцию "перевод" в объект "счет" было бы неверно, поскольку в опера­ особенного, а просит два объекта "банковский счет" выполнить большую часть работы .

ции участвуют два счета и ряд глобальных регламентов .

Теоретически можно было бы создать объект Перевод (Funds Transfer), пред­ ставляющий два банковских счета плюс регламентные правила и история перевода. Но в межбанковских сетях все равно никуда не деться от вызова СЛУЖБ. Более того, в большинстве сред разработки создавать прямой интерфейс между объектом пред­ метной области и внешними ресурсами - это нарушение стиля программирования .

Внешние СЛУЖБЫ можно "задрапировать" ФАСАДНЫМ МЕТОДОМ ( FACADE), на вход которого поступают объекты модели, а возвращается в качестве результата, например объект Перевод. Но какие бы у нас ни были посредники (пусть даже не из нашей сис­ темы), эти СЛУЖБЫ все равно выполняют обязанности из предметной области по пе­ реводу денежных средств .

–  –  –

Степень м одуль ности Хотя этот раздел в основном посвящен тому, чтобы подчеркнуть, как выразительно и удобно некоторые понятия модели реализуются в виде СЛУЖБ, сам по себе этот шаблон ценен тем, что помогает управлять крупностью разбиения (степенью модульности) в ин­ терфейсах уровня предметной области, а также изоляцией клиентов от СУЩНОСТЕЙ и ОБЪЕКТОВ-ЗНАЧЕНИЙ .

Средней крупности СЛУЖБЫ, не имеющие собственного состояния, легче переносить в большие системы, поскольку в них за простым интерфейсом инкапсулируются боль­ шие функциональные возможности. Если же объекты имеют слишком мелкий масштаб, в распределенной системе это может привести к неэффективному обмену сообщениями .

Как говорилось ранее, из-за слишком мелких объектов предметной области может произойти утечка знания на операционный уровень, где координируется работа объектов модели. Сложность взаимодействия, слишком загроможденного мелкими деталями, в конце концов, передается на операционный уровень. Знание из модели потихоньку "растекается" по коду прикладных операций или пользовательского интерфейса, уходя с положенного ему уровня. Разумное же применение служб уровня предметной области позволяет удерживать четкую границу между уровнями .

Б этом архитектурном шаблоне простоте интерфейса отдается предпочтение перед разно­ образием функций и управлением со стороны клиента. Разбиение (модульность) функцио­ нальных возможностей поддерживается на среднем уровне, как раз удобном для инкапсули­ рования компонентов в больших или распределенных системах. К тому же, иногда СЛУЖБА это самый естественный способ выразить понятие из модели предметной области .





До ступ к службам в распределенных системных архитектурах, таких как J2EE и CORBA, имеется спе­ циальный механизм объявления СЛУЖБ вместе с соглаlllениями по их использованию, а также функциями распределения и доступа. Но такие среды не всегда используются в программных проектах, а даже если и используются, то в таком простом деле, как логи­ ческое разделение обязанностей, это будет стрельба из пушки по воробьям .

ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ П РЕДМЕТНО-ОРИЕНТИРОВАННОГО .

. .

Не так важны конкретные средства для обеспечения доступа к службе, как базовое про­ ектное решение разделить те или иные обязанности. Для реализации интерфейса какой­ нибудь СЛУЖБЫ вполне может подойти и объект-"агент". Например, для обеспечения дос­ тупа можно легко написать простой объект типа SINGLETON [14]. С помощью стилевых со­ глашений легко выделить эти объекты так, чтобы стало ясно - это просто механизмы для создания интерфейса СЛУЖБ, а не важные объекты модели. Сложные архитектуры следует применять только тогда, когда есть реальная потребность в сетевом распределении системы или другом использовании мощных средств архитектурной среды .

Модули (пакеты ) МОДУЛЬ (MODULE) - это устоявшийся элемент архитектуры программ. Кроме тех­ нических соображений, при разбиении на модули в основном руководствуются стремле­ нием уменьшить СМЫСЛОВУIО сложность той или иной части программы. МОДУЛИ дают возможность посмотреть на модель с разных сторон: во-первых, можно изучить подроб­ ности устройства модуля, не вникая в сложное целое; во-вторых, удобно рассматривать взаимоотношения между модулями, не вдаваясь в детали их внутреннего устройства .

модели, выражая суть и структуру модели в крупном масштабе .

На уровне предметной области МОДУЛИ должны соответствовать смысловым частям ** * МОДУЛИ используют все, но мало кто воспринимает их как полноценные структур­ ные составляющие модели. Код подразделяют по разным критериям на всевозможные виды структурных элементов: от отдельных аспектов технической архитектуры до ра­ бочих заданий конкретных разработчиков. Но даже те разработчики, которые MHoro занимаются рефакторинrом, склонны не нарушать rраниц МОДУЛЕЙ, установленных на ранних этапах проекта .

То, что при делении на модули должна соблюдаться низкая внешняя зависимость (low coupling) при высокой внутренней связности (high cohesion) - это общие слова .

Определения зависимости и связности rрешат уклоном в чисто технические, количест­ венные критерии, по которым их якобы можно измерить, подсчитав количество ассо­ циаций и взаимодействий. Но это не просто механические характеристики подразде­ ления кода на модули, а идейные концепции. Человек не может одновременно удер­ живать в уме слишком MHoro предметов (отсюда низкая внешняя зависимость) .

А плохо связанные между собой фраrменты информации так же трудно понять, как неструктурированную "кашу" из идей (отсюда высокая внутренняя связность) .

Низкая внешняя зависимость и высокая внутренняя связность - это общие принципы программной архитектуры, применяемые как к отдельным объектам, так и к МОДУЛЯМ. Но особенную важность они приобретают в крупном масштабе моделирования архитектуры .

Эти термины существуют уже долгое время; изложение на эту тему в стиле описания архи­ тектурных шаблонов можно найти в [17] .

Когда два элемента модели разводятся по разным модулям, взаимосвязь между ними становится не такой прямой, как раньше. Из-за этого бывает труднее понять, какое место они занимаIОТ в архитектуре программы. Если минимизировать зависимость между МОДУЛЯМИ, задача облегчается, и становится RОЗМОЖIIЫМ анализировать содержимое одного МОДУЛЯ, почти не обращаясь к тем другим, с которыIии он взаимодействует .

В то )ке время элементы хорошей модели склонны к синергизму, и если подразделе­ ние на МОДУЛИ выбрано удачно, то взаИМООТНОIllения между элементами модели выхо­ дят на новый концептуальный уровень. Высокая связность между объектами родственнГЛАВА 5. МОДЕЛЬ, В Ы РАЖЕННАЯ В ПРОГРАММЕ 111 ного назначения позволяет сосредоточить работу по моделированию и архитектурному проектированию в пределах одного МОДУЛЯ, т.е. на таком уровне сложности, который человеческому уму вполне по плечу .

МОДУЛИ и меньшие структурные элементы должны эволюционировать вместе, но на практике так обычно не получается. МОДУЛИ выбираIОТСЯ на ранней стадии работы, что­ бы придать организованность первоначальным версиям объектов. После этого объекты имеют тенденцию развиваться так, чтобы не нарушать границ уже установленного опре­ деления МОДУЛЯ. Рефакторинг модулей требует больших затрат труда и более опасен, чем рефакторинг классов, да и делать его так же часто, скорее всего, не удастся. Но как объекты модели вначале выглядят наивно и слишком конкретно, а со временем преобра­ жаются и начинают выражать более глубокое понимание предмета, так и МОДУЛИ могут выйти на более высокий уровень точности и абстракции. Если дать МОДУЛЯМ возмож­ ность отражать изменяющееся понимание предмета, то и у объектов внутри них появит­ ся возможность более гибкой эволюции .

Как и все остальное в искусстве DDD, МОДУЛИ являются механизмом коммуникации .

Выбор разбиения на МОДУЛИ должен основываться на смысле объектов, подвергающихся разделению. Если вы помещаете несколько классов в один МОДУЛЬ, вы тем самым гово­ рите вашему преемнику, которому предстоит изучать эту архитектуру: думай о них вме­ сте, как о целом. Если ваша модель рассказывает историю, то МОДУЛИ это ее главы .

Имя МОДУЛЯ должно передавать его содержание и входить в ЕДИНЫЙ ЯЗЫК модели .

Можно сказать специалисту в предметной области: "а сейчас давайте поговорим о моду­ ле Кnиент", и для разговора сразу же создается контекст .

Выберите такие МОДУЛИ, которые бы рассказывали историю системы и содержали связные наборы понятий. От этого часто сама собой возникает низкая зависимость МОДУЛЕЙ друг от друга. Но если это не так, найдите способ изменить модель таким об­ разом, чтобы отделить понятия друг от друга, или же поищите пропущенное в модели понятие, которое могло бы стать основой для МОДУЛЯ и тем самым свести элементы модели вместе естественным, осмысленным способом. Добивайтесь низкой зависимо­ сти модулей друг от друга в том смысле, чтобы понятия в разных модулях можно было анализировать и воспринимать независимо друг от друга. Дорабатывайте модель до тех пор, пока в ней не возникнут естественные границы в соответствии с высокоуров­ невыми концепциями предметной области, а соответствующий код не разделится со­ ответствующим образом .

Дайте модулям такие имена, которые войдут в ЕДИНЫЙ ЯЗЫК. Как сами МОДУЛИ, так и их имена должны отражать знание и понимание предметной области .

Анализ концептуальных взаимосвязей не заменяет технической работы. Это разные ас­ пекты одной и той же проблемы, и заниматься придется ими всеми. Но мышление на осно­ ве модели порождает более глубокое и качественное решение, чем сиюминутные меро­ приятия. Если возникает проблема выбора, лучше придерживаться концептуальной ясно­ сти, пусть даже от этого между МОДУЛЯМИ будет больше внешних ссылок или же при внесении изменений проявятся какие-нибудь мелкие побочные эффекты. Разработчики справятся с этими мелочами, если они понимают, что именно им рассказывает модель .

** * Гиб кая м одульность МОДУЛЯМ необходимо эволюционировать вместе с остальными компонентами моде­ ли. Это означает, что над МОДУЛЯМИ, как и над моделью с кодом, тоже нужно выполнять как правило, нужно вносить в код широкомасштабные изменения. Такие изменения МОрефакторинг. Но такой рефакторинг часто не делают вовсе. Чтобы изменить МОДУЛИ, ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННО ГО.. .

оказаться пагубными для коммуникации в группе разработчиков, и даже нарушить rYT работу средств разработки, например, систем управления исходным кодом. В результате структура и имена МОДУЛЕЙ часто отражают гораздо более ранние "очертания" модели, чем это делают классы .

Неизбежные ошибки, сделанные на ранних этапах разработки, приводят к высокой взаимозависимости МОДУЛЕЙ, что затрудняет рефакторинг. А недостаточный рефакто­ ринг - это способ оттягивать реlпение проблемы. Решить ее можно, только стиснув зубы где в коде находятся проблемные места .

и реорганизовав модули. При этом надо основываться на опыте, который подсказывает, Некоторые средства разработки и системы программирования только усугуБЛЯIОТ проблему. Но какая бы технология разработки не применялась для реализации про­ граммы, необходимо найти способ минимизировать работу по рефакторингу МОДУЛЕЙ и устранить препятствия, мешаЮlцие коммуникации в группе .

Пример

Правила программирования пакетов в Java В языке]аvа импорт, т.е. подключение других модулей, нужно объявлять в отдельных классах. Моделировщик может себе позволить думать о взаимосвязях между пакетами (packages), но объявить это непосредственно в Java нельзя. Согласно распространенным правилам, рекомендуется импортировать отдельные классы, так что получается пример­ но следующее .

ClassAl import packageB.ClassBl;

import packageB.ClassB2;

import расkаgеВ.СlаssВЗ;

import packageC.ClassCl;

import packageC.ClassC2;

import расkаgеС.СlаssСЗ;

к сожалению, в ]ava никуда не деться от импортирован ия в отдельные классы. Но можно хотя бы импортировать целые пакеты за раз, предполагая, что они являются ВЫ­ сокосвязными модулями, И одновременно уменьшая потенциальный объем работы по переименованию пакетов .

ClassAl import packageB.*;

import packageC.*;

Правда, в этом приеме смеlпиваIОТСЯ два разных масштаба модульности ( классы им­ портируют пакеты), но и информации сообщается больше, чем в предыдущем объеми­ стом списке классов - здесь декларируется намерение установить связь (создать зави­ симость) с некоторым МОДУЛЕМ .

Если же конкретный класс действительно зависит от отдельного класса из другого пакета, а его МОДУ ЛЬ не показывает ярко выраженной концептуальной зависимости от того, другого МОДУЛЯ, то, возможно, следует или переместить класс, или даже пересмот­ реть всю структуру этих МОДУЛЕЙ .

ГЛАВА 5. МОДЕЛЬ, ВЫРАЖЕН НАЯ В ПРО Г РАММЕ

Ловушки инфраструктур ы Сильное влияние на проектные решения по организации пакетов оказываIОТ техниче­ ские среды программирования. Иногда это влияние благотворно, а иногда его нужно ос­ терегаться .

Пример очень полезного стандарта, задаваемого средой - принудительное введение МНОГОУРОВНЕВОЙ АРХИТЕКТУРЫ путем помещения кода инфраструктуры и пользова­ тельского интерфейса в разные группы пакетов. Вследствие этого уровень предметной области (модели) тоже физически выделяется в свой, отдельный набор пакетов .

С другой стороны, многоярусные архитектуры могут привести к слишком фрагмен­ тированной реализации объектов модели. В некоторых средах многоуровневость созда­ ется таким образом, что обязанности одного объекта модели распределяются между мно­ гими объектами про граммы, которые еще и помещаются в разные пакеты. Например, обычная практика в J2EE - поместить данные и средства доступа к ним в объект Java Веап (entity Ьеаn), а связанные с ними деловые регламенты и прикладные операции­ в сеансовый объект Java Веап (session Ьеаn). Не считая дополнительных сложностей реа­ лизации каждого компонента, это разделение еще и лишает объектную модель связности и согласованности. Один из фундаментальных принципов объектно-ориентированного программирования состоит в том, что в объекте должны инкапсулироваться и данные, и операции над этими данными .

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

На этом этапе мысленно собирать разные объекты в одну концептуальную СУЩНОСТЬ (ENTITY) уже отнимает слишком много труда. Теряется связь между моделью и архитекту­ рой программы. Хорошим стилем тут будет применение более крупномасштабных компо­ нентов EJB, чем объекты-СУЩНОСТИ, чтобы уменьшить тем самым побочные эффекты разделения уровней. Но и мелкомасштабные объекты тоже часто разбиваются на уровни .

С такими проблемами я столкнулся, например, в одном довольно неплохо управляе­ мом проекте, в котором каждый концептуальный объект разбивался на четыре уровня .

И каждое из таких разбиений имело веское обоснование. Первый уровень управлял хра­ нением и отображением данных, а также доступом к реляционной базе. Затем шел уро­ вень, управлявший характерными, универсальными операциями объектов. Далее нахо­ дился уровень, накладывавший на все это специфические для приложения операции .

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

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

Но в дополнение ко всему этому архитектурная среда программирования требова­ ла, чтобы каждый уровень находился в отдельном наборе пакетов с именами, соответ­ ствующими правилам идентификации для этого уровня. Теперь разделение уже не помещалось в голове. В результате разработчики предметной области старались завес­ ти поменьше МОДУЛЕЙ (учитывая, что их количество надо было умножать на четыре), и почти никогда не изменяли ни одного из них, поскольку рефакторинг любого МОДУЛЯ требовал слишком много усилий. Что еще хуже, отследить все данные и опе­ рации, относящиеся к одному концептуальному объекту, стало так трудно (учитывая еще и непрямой характер деления на уровни), что разработчикам некогда было думать 1 14 ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

о каких-то там моделях. Программа все-таки была написана и сдана заказчику. Но ее "чахлая" модель предметной области свелась к тому, чтобы удовлетворить требования приложения к работе с базой данных и распределить все операции между несколькими СЛУЖБАМИ. Почти никак не проявились преимущества ПРОЕКТИРОВАН ИЯ ПО МОДЕЛИ (MODEL- DRIVEN DESIGN), потому что модель не настолько прямо выражалась в коде, чтобы разработчик мог с нею работать .

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

Движущим мотивом для применения таких схем разбиения на пакеты еще называют сетевую распределенность уровней. Это сильный довод в тех случаях, когда код действи­ тельно устанавливается на разных серверах. Но обычно это не так. А гибкости в этом смысле стоит добиваться только тогда, когда это действительно необходимо. В проекте, где есть надежда воспользоваться преимуществами ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ, такая жертва слишком велика, если только она не решает наболевшую, не терпящую отлага­ тельства проблему .

Сложные схемы разбиения на пакеты, баЗИРУЮlциеся на технических соображениях, имеют две особенности:

если требования архитектурной среды к распределению обязанностей таковы, что • элементы, реализующие концептуальные объекты, оказываются физически разде­ ленными, то код больше не выражает модель;

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

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

те весь ко д, реализуюЩИЙ один КОlЩептуальный объект, в одном МОДУЛЕ, а то и классе .

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

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

Существуют и другие ловушки, грозящие принципу ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ из-за правил и соглашений, принятых в архитектурных средах программирования, ком­ паниях или отдельных проектах. Из-за них тоже может пострадать естественная связ­ ность объектов предметной области, и вывод будет таким же. Наличие строгих правил или чрезмерное количество обязательных пакетов часто сводит на нет возможность реа­ лизации других схем модульного разбиения, специально построенных для нужд модели предметной области .

ГЛАВА 5. МОДЕЛЬ, ВЫРАЖЕНН АЯ В ПРОГРАММЕ 1 15 И спользуйте разбиение на пакеты только для того, чтобы отделить уровень пред­ метной о бласти от остального кода .

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

Исключение можно сделать в том случае, если код генерируется на основе деклара­ тивной архитектуры (см. главу 10). В этом случае разработчику нет нужды читать код, и его лучше поместить в отдельный пакет, чтобы он не мешал разработчикам выстраи­ вать архитектурные элементы программы .

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

Каждое понятие модели должно найти свое отражение в элементе реализации .

СУЩНОСТИ, ОБЪЕКТЫ-ЗНАЧЕНИЯ, ассоциации между ними, а также несколько СЛУЖБ уровня предметной области и МОДУЛИ, служащие для организации всего этого, - в них ус­ танавливается прямое соответствие между программной реализацией и моделью. Объекты, указатели и механизмы извлечения данных в реализации должны соответствовать модели напрямую и самым очевидным образом. Если это не получается, приведите в порядок код, вернитесь на несколько шагов назад, измените модель - или сделайте все это вместе .

Боритесь с ИСКУlпением нагрузить объекты предметной области чем-нибудь таким, что непосредственно не относится к понятиям, которые они представляют. Помните, что дель. Существуют и другие относящиеся к предметной области обязанности, и их надо у этих элементов программной архитектуры уже есть обязанности - они выражают мо­ выполнять; существуют и другие данные, и их нужно обрабатывать для слаженной рабо­ ты всей системы. Но объекты модели не обязаны тащить все это на себе. В главе 6 мы рассмотрим некоторые вспомогательные объекты, выполняющие технические обязанно­ сти уровня предметной модели - например, задание поиска по базе данных и инкапсу­ ляция создания сложных объектов .

Четыре архитектурных шаблона, представленных в этой главе, - это структурные единицы, "кирпичики" объектной модели. Но в ПРОЕКТИРОВАНИИ ПО МОДЕЛИ не обяза­ тельно насильно впихивать все на свете в формат объектов. Существуют и другие пара­ дигмы моделирования, поддерживаемые средами программирования - например, сервер nрwzожеllUЙ ( business rule engine). В проектах приходится идти на прагматические ком­ промиссы между такими парадигмами. Но все эти среды, архитектуры и приемы - толь­ ко средства для ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ, а не замена ему .

Парадигмы м оделир ов ания ПРОЕКТИРОВАНИЕ ПО МОДЕЛИ требует такой технологии реализации, которая со­ гласовывалась бы с конкретной парадигмой моделирования, применяемой в проекте .

Эксперименты проводились со многими парадигмами, но всего несколько из них оста­ лись в широком употреблении. В настоящее время основная парадигма - объектно­ ориентированная. В основе разработки наиболее сложных проектов лежит именно ра­ бота с объектами. Объектно-ориентированный подход стал доминировать по ряду причин. Частично сыграли свою роль естественные свойства объектов, частично так ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО .

. .

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

Причины доминирования объ ектной парадигмы Разработчики не обязательно выбирают объектную парадигму по техническим при­ чинам или ради идейных преимуществ объектов. Но нам важно не разнообразие моти­ вов, а то, что объектное моделирование действительно предлагает неплохой баланс про­ стоты В работе и возможности сложной организации систем .

Если парадигма моделирования слишком трудна для восприятия, ее не сможет осво­ ить достаточное количество разработчиков, и использоваться она будет неправильно .

Если члены группы, непосредственно не связанные с кодом, не будут владеть хотя бы ос­ новами парадигмы, они не поймут модель, и в проекте потеряется ЕДИНЫЙ ЯЗЫК. Прин­ ципы объектно-ориентированного программирования большинству людей кажутся есте­ ственными. Хотя некоторые программисты теряются в тонкостях моделирования, даже "не-технари" способны понять графическую схему объектной модели .

Несмотря на простоту концепции объектного моделирования, она оказалась доста­ точно емкой для передачи важных знаний о предметной области. С самого своего воз­ никновения она поддерживалась набором средств программирования, с помощыо кото­ рого модель можно было выразить в программе .

В настоящее время объектная парадигма также имеет ряд значительных ситуационных преимуществ, которые про истекают из ее развитости и широкой распространенности. Без развитой инфраструктуры и инструментальной поддержки проект может стать похожим на научно-исследоватеЛЬСКУIО работу по новым технологиям программирования. Бо-первых, на это уйдут время и ресурсы, которые надо было бы употребить на основную разработку, а во-вторых, появится лишний технический риск. Некоторые технологии между собой плохо совместимы, и может оказаться, что интегрировать их в одно целое не удастся с по­ мощью стандартных профессиональных решений, отчего придется заново изобретать мно­ гие нужные вещи. Но за долгие годы многие из этих проблем для объектно-ориентирован­ ных сред либо уже решены, либо благодаря широкому распространению этого подхода ста­ ли неактуальны. (Теперь уже другие парадигмы вынуждены подстраиваться, чтобы иметь возможность интегрироваться с объектной.) Большинство новых технологий изначально включает в себя средства интеграции с популярными объектно-ориентированныltlии платфор­ мами. Это не только облегчает интеграцию, но и часто дает дополнительную возможность смешивания в одной системе нескольких подсистем, построенных на разных парадигмах моделирования (о чем мы поговорим позже в этой главе) .

Не менее важна также достаточная степень развития сообщества разработчиков и культуры nроектированuя программных архитектур. Для проекта, в котором принима­ ется новая парадигма, иногда не удается найти разработчиков с достаточными знаниями нужных технологий или опытом построения эффективных моделей для выбранной па­ радигмы. Не исключено, что будет невозможно обучить программистов за разумное вре­ мя, потому что еще не устоялись типовые архитектурные Iпаблоны для построения большей части парадигмы и технологии. Новаторы в этой области, может, и могли бы справиться с задачей, но они еще не опубликовали свои наработки в доступной форме .

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

Проиллюстрировать рискованность работы с неразвитой парадигмой можно на приме­ ре одного проекта, разрабатывавшегося около полутора десятилетий тому назад. Б начале ГЛАВА 5. МОД ЕЛЬ, ВЫРАЖЕНН АЯ В ПРО ГРА ММЕ 1 17 90-х годов для этого проекта были приняты на вооружение несколько самых передовых технологий, в том числе широкое использование объектно-ориентированной базы данных .

Это было очень увлекательно. Гостям проекта с гордостью рассказывали, что создается и разворачивается самая большая база данных, когда-либо поддерживавшаяся в рамках этой технологии. Когда я присоединился к проекту, несколько разных групп уже мастери­ ли объектно-ориентированные архитектуры и безо всякого труда помещали свои объекты в базу. Но постепенно на нас снизошло понимание того, что мы уже заняли значительную часть допустимого объема базы - и это еще только тестовыми данными! А реальная база должна была быть в десятки раз объемнее, и объем транзакций, проходящих через нее, в десятки раз больше. Так возможно ли применить данную технологию в этой разработке?

Использовали ли мы ее неправильно? Это было выше нашего понимания .

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

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

С помощью привлеченного специалиста мы усовершенствовали инфраструктуру .

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

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

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

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

Вот почему в настоящее время в большинстве проектов, где планируется применять П РОЕКТИРОВАНИЕ ПО МОДЕЛИ, разумно будет выбрать объектно-ориентированную тех­ нологию в качестве ядра системы. При этом чисто объектной системой ограничиваться 1 18 ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

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

Впрочем, это не значит, что мы всегда обязаны выбирать объектно-ориентированный подход. Идти вместе с толпой, может, и безопаснее, но не всегда это правильный выбор .

Объектные модели позволяют адекватно решать огромное количество задач практического программирования, но существуют такие области, которые неестественно моделировать дискретными смысловыми единицами с инкапсулированными в них операциями. Напри­ мер, предметные области со сложной математикой или преобладанием глобальных логиче­ ских построений не всегда хорошо вписываются в объектно-ориентированную парадигму .

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

Какова бы ни была основная парадигма моделирования в проекте, в предметной облас­ ти непременно будут такие аспекты, которые легче выразить в рамках другой парадигмы .

Если в предметной области совсем немного элементов, которые не вписываются в приня­ тую (и подходящую) парадигму, то разработчики вполне могут смириться с наличием не­ скольких "неуЮIЮЖИХ" объектов в модели, которая в остальном выглядит стройно и согла­ сованно. (В случае другой крайности - если большая часть модели более естественно опи­ сывается в рамках другой парадигмы - может быть, ИlVlеет смысл перейти на эту другую парадигму и сменить платформу реализации?) Но если достаточно крупные фрагменты модели относятся к другим парадигмам, разумно будет в каждом частном случае принять смешанного набора средств разработки. Если взаимосвязи и зависимости не слишком тес­ на вооружение подходящие парадигмы, а программную реализацию делать с применением ные, можно ограничиться инкапсуляцией подсистемы, построенной на другой парадиг­ ме, - например, просто вызвать из объекта процедуру сложных математических расчетов .

l-Io бывает и так, что разные аспекты модели связаны более тесно, - например, если взаи­ модействие между объектами зависит от неких математических соотношений .

Именно поэтому в объектные системы часто приходится интегрировать такие не­ объектные компоненты, как сервер nрwzоженuй (business rules engine) и сервер делопроиз­ водства (workflow engine). Смешивание парадигм позволяет разработчикам моделиро­ вать те или иные понятия таким способом, который ЛУЧllIе всего для них подходит. К то­ му же, в большинстве систем все равно приходится использовать необъектные элементы технической инфраструктуры, - как правило, реляционные базы данных. Но построить единообразную модель, подчиняющуюся нескольким парадигмам, нелегко, и обеспечить сосуществование технических средств программирования тоже задача непростая. Если разработчики не видят четкого выражения модели в программе, ПРОЕКТИРОВАНИЕ ПО МОДЕЛИ "приказывает долго жить" - пусть даже смешение парадигм и инфраструктур­ ных средств требует именно его .

ПРО ЕКТИРОВАНИЕ ПО МОД ЕЛИ в условиях смешения парадигм Здесь в качестве примера технологии, иногда "примешиваемой" к объектно-ориен­ тированным приложениям, будет служить сервер приложений (rules engine), реализуюГЛАВА 5. МОДЕЛЬ, ВЫРАЖЕННАЯ В ПРОГРАММЕ 1 19 щий деловые регламенты из определенной области. Информоемкая модель предметной области вполне может содержать явно прописанные регламентные правила, вот только объектная парадигма лишена нужных семантических средств для формулировки правил и связей между ними. Конечно, регламенты можно моделировать объектами, и это часто делается не без успеха, но их инкапсуляция в объектах мешает применять глобальные правила, распространяющиеся на всю систему. Технология серверов приложений не ли­ шена привлекательности, поскольку обещает более естественный и декларативный способ определения правил, при котором парадигма регламентов может смеПIиваться с объектной парадигмой. Парадигма логики (регламентированных правил) хорошо разработана и вы­ сокоэффективна, и есть основания полагать, что она может послужить ХОРОIJlИМ допол­ нением к сильным и слабым местам объектов .

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

Работая с деловыми регламентами, важно не прекращать думать в терминах модели .

Разработчикам необходимо найти единую модель, которая годилась бы для обеих пара­ дигм реализации. Это нелегко, но возможно, если сервер приложений допускает вырази­ тельную СМЫСЛОВУIО реализацию. В противном случае данные и регламенты оказывают­ ся несвязанными. Регламентные правила в составе сервера начинают напоминать, ско­ рее, маленькие программки, чем концептуальные правила Iодели предметной области .

Но если между регламентами и объектами установлены тесные и четкие ОТНОllIения, то смысл обеих частей системы сохраняется .

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

Наиболее эффективный инструмент сбора частей в одно целое - это надежный ЕДИНЫЙ ЯЗЫК, стоящий за неоднородной моделью. Последовательное применение имен и терминов в двух разных средах и введение :JТИХ имен в ЕДИНЫЙ ЯЗЫК помогает запол­ нить смысловой пробел .

Эта тема сама по себе заслуживает отдельной книги. Цель же этого раздела состоит в том, чтобы показать: нет причин отказываться от ПРОЕКТИ РОВАНИЯ ПО МОДЕЛИ, и стоит приложить все усилия, чтобы удержаться на этом пути .

Хотя ПРОЕКТИРОВАНИЕ ПО МОДЕЛИ не обязано быть объектно-ориентированным, в нем все-таки нельзя обойтись без выразительной реализации конструкций модели ­ будь то объекты, регламенты или процедуры делопроизводства. Если выбранные для ра­ боты программные средства не обладают нужной выразительностью, пересмотрите этот выбор. Невыразительная программная реализация сводит на нет преимущества допол­ нительной парадигмы .

Ниже даны четыре основных правила для ввода необъектных элементов в преимуще­ ственно объектно-ориентированную систему .

Не противостоять nарадИlМе реализации. Всегда есть другой способ посмотреть на • предметную область. Найдите концепции, которые бы соответствовали парадигме .

–  –  –

ским средством, таким как UМL-диаграммы, заставляет разработчиков искажать модель в угоду удобству вычерчивания схем. Б UML есть кое-какие средства для представления, например, связей-ограничений, но их не всегда достаточно. Лучше прибегнуть к граq)ическим схемам другого вида (может быть, вполне обычным для другой парадигмы) или описаниям на обычном человеческом языке, чем му­ чительно адаптироваться к схемам одного частного вида, передающим только один ИЗ взглядов на объектные модели .

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

Регламентные правила можно выразить в виде объектов, пусть и не так аккуратно, а вот одновременное применение нескольких парадигм все усложняет многократно .

пать все возможности в пределах доминирующей парадигмы. ПУСТЬ некоторые понятия Прежде чем взваливать на себя груз нескольких смешанных парадигм, следует исчер­ модели и не представляют собой очевидных объектов - все равно их часто можно про­ моделировать в пределах парадигмы. Б главе 9 будет рассматриваться моделирование необычных видов понятий моделей с использованием объектной технологии .

Особый случай смешения парадигм представляет собой реляционная парадигма. Бу­ Il.учи самой распространенной необъектной технологией, реляционная база данных в то же время более тесно связана с объектной моделыо, чем другие компоненты, потому что служит постоянным хранилищем данных, образующих в совокупности сами объекты .

Хранение объектных данных в реляционных базах, как и многие другие проблемы цикла существования объектов, будет рассматриваться в главе 6 .

–  –  –

К аждый объект имеет свой цикл существования. Объект "рождается", затем, как правило, проходит ряд сменяющихся состояний, а потом "умирает" - либо уда­ ляется, либо помещается в архив. Многие из объектов - простые и временные; они соз­ даются после вызова конструктора, используются в вычислительных операциях, после чего их оставляют сборщику мусора. Усложнять такие объекты ни к чему. Но у некото­ рых объектов существование может продлиться намного дольше и частично пройти не в оперативной памяти. Они имеют сложные связи и отношения с другими объектами и проходят через изменения состояния, подчиняющиеся определенным инвариантам .

вести к нарушению принципов ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ (MODEL-DRIVEN DESIGN) .

Управление такими объектами не обходится без трудностей, которые легко могут при­

–  –  –

Эти трудности делятся на две категории .

Поддержание целостности объекта на этапе его существования .

1 .

Предотвращение излишней сложности в управлении циклом существования 2 .

объектов .

В этой глане указанные проблемы будут решаться на основе трех архитектурных шаблонов. Во- первых, АГРЕГАТЫ (AGGREGATES) помогут "подтянуть" саму модель и из­ бавиться от хаотической путаницы разнообразных объектов, четко определив права соб­ ственности и границы. Этот шаблон играет решаlОЩУIО роль в поддержании целостности объектов на всех этапах их существования .

Далее мы сместим акцент на начало цикла СУIцествования и воспользуемся ФАБРИКАМИ ( FACTORIES) для создания и восстановления сложных объектов и АГРЕГАТОВ с сохранением инкапсуляции их внутренней структуры. Наконец, середина и конец жизненного цикла и извлекать I I ОСТОЯННО хранимые объекты, при этом инкапсулируя гигаНТСКУIО инфра­ об ьектов - это компетенция ХРАНИЛИIЦ (REPOSITORIES), которые ПОЗВОЛЯIОТ находить структуру, СТОЯIЦУIО за этими операциями .

Хотя ХРАI IИЛJlIЩА и ФАБРИКИ непосредственно не происходят из предметной облас­ ти, они играют ваЖIIУIО СМЫСЛОВУIО роль в ее архитектуре. Эти конструкции - ценное подспорье в ПРОЕКТИ РОВАНИ И ПО МОДЕЛИ, IIОСКОЛЬКУ они даIОТ нам в руки удобные средства обращения с объектами модели .

дает нам возможность манипулировать объектами систематически, в естественных смы­ Моделирование АГРЕГАТОВ наряду с добавлением в программу ФАБРИК и ХРАНИЛИЩ словых границах, на протяжении всего цикла их существования. АГРЕГАТЫ обозначают об­ ласть действия, в пределах которой на каждом этапе жизненного цикла должны удовлетво­ ряться определенные инварианты. ФАБРИКИ и ХРАНИЛИЩА оперируют АГРЕГАТАМИ, ин­ капсулируя сложности специq)ических переходных этапов их существования .

Агр егаты

Если снизить до м инимума количество вводимых в модель ассоциаций, становится легче проследить взаимосвязи между I I О НЯТИЯМИ и хотя бы немного ограничить их лави­ нообразный рост. Но большинство практических предметных областей обладает такой богатой внутренней связностыо, что в конце концов все равно приходится трассировать 1 24 ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕ МЕНТЫ ПРЕД М ЕТНО-ОРИЕНТИРОВАННОГО.. .

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

Предположим, мы удаляем объект Человек (Person) из базы данных. Вместе с ним удаляется имя, дата рождения, описание его работы. А как насчет адреса? По тому же ад­ ресу могут проживать и другие ЛIОДИ. Если удалить адрес, соответствующие объекты Человек будут содержать ссылки на удаленный объект. А если оставить, в базе будут накапливаться отработанные адреса. Автоматическая сборка мусора могла бы ликвиди­ ровать эти адреса, но даже если такая возможность есть в СУБД, это чисто техническая мера, - а принципиальный вопрос моделирования остается нерешенным .

Даже для отдельной транзакции трудно предсказать, до каких пределов может про­ стираться ее влияние, учитывая переплетение взаимосвязей в типичной объектной мо­ дели. А обновлять каждый объект в системе на случай наличия какой-то связи - не Проблема особенно обостряется в системах с параллельным доступом к одним и тем слишком практично .

же объектам со стороны многочисленных клиентов. В случае, когда сразу много пользо­ вателей могут просматривать и изменять различные объекты в системе, приходится ду­ мать, как предотвратить одновременные модиq)икации взаимозависимых объектов. Если неправильно оценить области определения и действия, можно оказаться в беде .

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

Сформулируем задачу по-другому. Откуда мы знаем, где начинается и где заканчива­ ется объект, составленный из других объектов? В любой системе с постоянным хранени­ ем данных у каждой транзакции, которая вносит в данные изменения, должна быть своя ограниченная область действия. Кроме того, должен быть способ поддерживать согласо­ ванность данных (путем соБЛlодения их инвариантов). В базах данных можно применять разные схемы блокирования, а также программировать тесты. Но эти половинчатые ре­ шения отвлекают нас от модели, и очень скоро положение только усугубится .

На самом деле, чтобы найти разумное решение проблем такого рода, требуется углуб­ ленное понимание предметной области, а именно таких ее параметров, как частота смены экземпляров того или иного класса. Необходимо построить модель, которая давала бы больше свободы в местах интенсивной передачи конкурирующих данных, но заставляла четко соБЛIодать строгие инварианты .

Эта проблема, на первый взгляд, кажется технической, связанной с транзакциями баз данных, но корни ее лежат в модели - в недостаточно определенных границах. Если вы­ вести решение из модели, она сама станет понятнее, и выразить ее в программной архи­ тектуре станет легче. По мере пересмотра модели соответствующие изменения будут вноситься и в програММНУIО реализаЦИIО .

СущеСТВУIОТ проработанные схемы для определения прав собственности/владения в модели. Описанная ниже простая, но строгая система, выведенная из них, предлагает набор правил для реализации транзакций, которые вносят изменения и в первичные объекты, и в те, которые ими владеют 1 .

1 Эту систему разработал и реализовал Дэвид Сигел (David Siegel) в проектах 90-х годов, но не опубли:ковал ее .

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАН ИЯ ОБЪЕКТОВ МОДЕЛИ

Прежде всего нам потребуется абстракция для инкапсуляции ссылок в пределах мо­ дели. Совокупность взаимосвязанных объектов, которые мы воспринимаем как единое целое с точки зрения изменения данных, называется АГРЕГАТОМ (AGGREGATE). У каждо­ го АГРЕГАТА есть '/(орневой обье'/(т и есть граница. Граница определяет, что находится внутри АГРЕГАТА. Корневой объект - это один конкретный объект-сУЩНОСТЬ ( ENTITY), содержащийся в АГРЕГАТЕ. Корневой объект - единственный член АГРЕГАТА, на который могут ссылаться внешние объекты, в то время как объекты, заключенные внутри грани­ цы, могут ссылаться друг на друга как угодно. СУЩНОСТИ, отличные от корневого объ­ екта, локально индивидуальны, но различаться они должны только в пределах АГРЕГАТА, поскольку никакие внешние объекты все равно не могут их видеть вне контекста корне­ вой СУЩНОСТИ .

Пусть, например, в авторемонтной мастерской используется программа, в которой построена модель автомобиля. Автомобиль представляет собой сущность с глобальной идентичностью - его необходимо отличать от всех остальных машин в мире, даже очень похожих. Для этой цели подходит номерной знак, поскольку это индивидуальный иден­ тификатор, присвоенный каждому автомобилю. Нам может понадобиться проследить пробег и степень износа. Чтобы точно знать, где какая шина, их тоже следует сделать историю использования шин в их четырех возможных положениях на колесах, узнать их СУЩНОСТЯМИ. Но очень маловероятно, что их индивидуальность будет нас интересовать вне контекста конкретного автомобиля. Если заменить шины и отослать старые на пере­ работку, то либо наша программа вообще перестанет их отслеживать, либо они станут анонимными членами кучи списанных шин. Их пробег уже никого не будет волновать .

Но более уместно к теме будет заметить, что даже если шины установлены на машине, никто не попытается ввести в систему запрос на поиск конкретной шины, чтобы потом посмотреть, на какой машине она стоит. Наоборот, в базу данных будет послан запрос на поиск машины, а потом запрос на временную ссылку, ведущую к шинам. Поэтому авто­ мобиль является корневой СУЩНОСТЬЮ в АГРЕГАТЕ, граница которого охватывает также и шины. С другой стороны, двигатели имеют собственные, выбитые на них серийные но­ мера, по которым их иногда разыскивают независимо от автомобилей. В некоторых при­

–  –  –

Из взаимосвязей между объектами АГРЕГАТА можно составить так называемые инва ­ рианты, т.е. правила совместности, которые должны соблюдаться при любых изменениЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ П РЕДМЕТНО-ОРИ ЕНТИРОВАННОГО.. .

данных. Не всякое правило, распространяющееся на АГРЕГАТ, обязано выполняться ях непрерывно. Восстановить нужные взаимосвязи за определенное время можно с помо­ щью обработки событий, пакетной обработки и других механизмов обновления системы .

Но соБЛIодение инвариантов, имеющих силу внутри агрегата, должно контролироваться немедленно по завершении любой транзакции .

–  –  –

Чтобы реализовать концепцию АГРЕГАТА в виде практической программной конст­ рукции, необходимо иметь набор правил, выполняющихся для любой транзакции .

Корневой объект-СУЩНОСТЬ имеет глобальную идентичность и несет полную от­ • ветственность за проверку инвариантов .

Некорневые объекты-СУЩНОСТИ имеют локальную идентичность - они уни­ • кальны только в границах АГРЕГАТА .

Нигде за пределами агрегата не может постоянно храниться ссылка на что-либо • внутри него, кроме его корневого объекта. Корневой объект-СУЩНОСТЬ может передавать ссылки на внутренние объекты-сУЩНОСТИ другим объектам. Но эти другие объекты могут использовать их только временно, и не имеют права хра­ нить их или как-то фиксировать. Корневой объект может передать копию ОБЪЕКТА-ЗНАЧЕНИЯ другому объекту, и что с ней потом случится не играет роли, поскольку это не более чем значение, и оно не имеет никаких связей с АГРЕГАТО М .

Как следствие из предыдущего правила, только корневые объекты АГРЕГАТОВ можно • непосредственно получать по запросам из базы данных. Все остальные объекты раз­ решается извлекать только по цепочке связей .

Объекты внутри АГРЕГАТА могут хранить ссылки на корневые объекты других • АГРЕГАТОВ .

–  –  –

в границах АГРЕГАТА. ( При наличии сборки мусора это просто. Поскольку внеш­ них ссылок ни на что, кроме корневого объекта, не существует, достаточно уда­ лить корневой объект, а остальное будет подчищено при сборке.) Как только вносится изменение в любой объект внутри границ АГРЕГАТА, следует • сразу удовлетворить все инварианты этого АГРЕГАТА .

Группируйте СУЩНОСТИ и ОБЪЕКТЫ-ЗНАЧЕНИЯ в АГРЕГАТЫ и опре деляйте границы каж д ого из них. Выберите о дин о бъект-СУЩНОСТЬ и сд елайте его корневым. О суще ­ ствляйте все обращения к о бъектам в границах АГРЕГАТА только черз его корневой о бъект. Разрешайте внешним объектам хранить ссылки только на корневой о бъект .

Ссылки на внутренние о бъекты АГРЕГАТА сле дует пере давать только во временное пользование, на время о дной операции. П оскольку д оступ к объектам АГРЕГАТА кон­ тролируется через корневой о бъект, неожиданные изменения внутренних о бъектов невозможны. В такой схеме разумно требовать удовлетворения всех инвариантов для о бъектов в АГРЕГАТЕ и для всего АГРЕГАТА в целом при любом изменении состояния .

Было бы очень удобно иметь такую среду программирования, которая бы позволяла объявлять АГРЕГАТЫ, автоматически реализуя схемы блокирования и т.п. Не имея такой поддержки, группа разработчиков должна иметь достаточно самодисциплины, чтобы вы­ работать соглашение об АГРЕГАТАХ и программировать в четком соответствии с ним .

П р имер Целостност ь товарного заказа Рассмотрим потенциальные осложнения, которые могут возникнуть в упрощенной системе приема товарных заказов .

–  –  –

На рисунке показана довольно обычная схема товарного заказа (ТЗ), разбитого на позиции, для которого введено правило-инвариант: суммарная стоимость всех позиций не может превышать предел, установленный для одного ТЗ в целом. В сущеСТВУIощей реализации имеется три взаимосвязанные проблемы .

ряет сумму и помечает себя как недопустимый, если превышен лимит. Как мы уви­ Удовлетворение инварианта. При добавлении новой позиции объект-заказ прове­ 1 .

–  –  –

ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО .

. .

Управление изменениями. Если ТЗ перемещается в архив или удаляется, позиции 2 .

уходят вместе с ним, но модель не дает никаких указаний, где нужно остановиться в следовании по цепочке взаимосвязей. Непонятно также, как именно на процесс ПаРШUlелъное обращение 'к базе данных. Обращения разных пользователей к базе влияет изменение цены того или иного товара в разное время .

з .

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

нам необходимо не дать им помешать друг другу. Начнем с очень простой схемы, ТЗ, и в которой мы блокируем любой объект, который начал редактировать пользователь, пока он не закончит свою транзаКЦИIО. Таким образом, пока Джордж правит позицию 00 1, Аманда н е имеет к ней доступа. Она может редактировать любую другую позицию в ЛIО­ бом другом ТЗ (ВКЛlочая и другие позиции из ТЗ, над которым работает Джордж) .

–  –  –

памяти каждого пользователя. Там их можно просматривать и редактировать. Блоки­ ровка базы запрашивается только тогда, когда начинается редактирование. Итак, и Джордж, и Аманда могут работать параллельно, пока они "не TporalOT" товарные пози­ ции друг друга. И все идет хорошо... пока Джордж и Аманда не приступаlОТ к работе над разными строками одного и того же товарного заказа .

–  –  –

Рис. 6.6. Одновременное редактирование, разные траюакции Пользователям и их программам кажется, что все хорошо, потому что они игнорируют изменения, внесенные в другие части базы данных во время их транзакций - ни одна из за­ блокированных одним пользователем позиций не влияет на работу другого пользователя .

–  –  –

После того как оба пользователя сохраняют свои изменения, в базу данных записыва­ ется ТЗ, в котором нарушается инвариант модели предметной области. Не соБЛlодено важное правило делового регламента. И никто даже не догадывается об этом .

Очевидно, блокирование одной позиции в заказе не является достаточной защитной

–  –  –

например, повысит лимит или уберет из заказа одну гитару. Итак, проблема предотвра­ щена. Это реIJlение может быть приемлемым, если работа в основном ведется одновре­ менно над множеством разных заказов. Но если обычно вместе редактируются разные позиции одного большого ТЗ, такая блокировка создаст неудобства .

Но даже в случае множества мелких заказов есть и другие способы нарушить инвари­ ант. Зайдем со стороны товара. Если кто-нибудь изменит цену на тромбоны, пока Аманда добавляет их в свой заказ, разве это не вызовет нарушения инварианта?

ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО .

. .

Попробуем заблокировать и товар в дополнение к целому ТЗ. И вот что получится, Аманда и Сэм работают над раз1lы.мu ТЗ .

если Джордж,

–  –  –

Итак, всем троим придется подождать .

В этот момент можно начать усовершенствование модели, добавляя в нее следующие знания из предметной области .

Товары используются во многих ТЗ (высокая конфликтность данных) .

1 .

Изменения в данные о товарах вносятся реже, чем в товарные заказы .

2 .

–  –  –

Если привести програММНУIО реализацию в соответствие с :JТОЙ моделью, то инвари­ ант для тз и его позиций будет выполняться гарантированно, а изменения в цене на то­ вар не обязаны немедленно влиять на позиции, которые ссылаются на этот товар. Более широкие правила совместности можно учесть другими способами. Например, система могла бы каждый день предлагать пользователю список устаревших цен, чтобы их можно было обновить или удалить. Но это не инвариант, за выполнением которого нужен стро­ гий контроль. Ослабляя взаимосвязь позиций в заказе и цен на товары, мы тем самым избегаем конфликтов данных и лучше отражаем реальные способы ведения дел. В то же время усиление взаимосвязи тз и его позиций гарантирует выполнение важного делово­ го регламента .

АГРЕГАТ вводит четкие права собственности на тз и его позиции таким способом, ко­ торый согласуется с реальной деловой практикоЙ. Создание и удаление тз и его позиций естественным образом объединены, тогда как создание и удаление товаров реализовано независимо .

*** АГРЕГАТЫ обозначают области действия, внутри которых на каждом этапе существо­ вания должны соБЛlодаться определенные инварианты. Описанные далее конструкции, инкапсулируя в себе некоторые сложные преобразования, выполняемые в жизненном а именно ФАБРИКИ (FACTORIES) и ХРАНИЛИIl{А (REPOSITORIES), опеРИРУIОТ АГРЕГАТАМИ, цикле объектов .

ЧАСТЬ 11. CTPYKTYPI-IbIЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАН НОГО .

. .

Фабрики Если создание объекта или целого АГРЕГАТА представляет большую сложность или открывает постороннему глазу слишком много внутренней структуры, то нужную ин­ каПСУЛЯЦИIО обеспечивают ФАБРИКИ (FACTORIES) .

*** значительной мере сила такого инструмента, как объекты, заключается в его сложном в внутреннем устройстве, ВКЛIОЧая и ассоциации между его элементами. Объект следует ет ОТНОIllения к самой его сути и его роли в транзакциях. С объекта довольно и этих обязанно­ "дистиллировать" (т.е. очищать от всего лишнего), пока в нем не останется ничего, что не име­ стей, свойственных середине цикла его существования. Если на сложный объект возложить еще и ответственность за создание самого себя, то начинают возникать проблемы .

Двигатель автомобиля - весьма непростос техническое устройство, в котором десят­ ки частей взаимодействуют друг с другом для выполнения главной задачи - вращения вала. Теоретически можно спроектировать такой двигатель, который сам бы брал порш­ и ввинчивались в них. Но маловероятно, чтобы такая сложная машина была такой же ни и вставлял их в цилиндры, а свечи зажигания в нем сами находили бы свои гнезда надежной и работоспособной, как обычный двигатель. Вместо этого мы считаем нор­ мальным, чтобы двигатель из частей собирал кто-то посторонний это мож:ет быть ав­ томеханик-человек или промышленный робот. l(aK робот, так и человек устроены значи­ тельно сложнее, чем двигатель, который они собирают. Работа по сборке из деталей не когда вы ведете его по дороге, вам не нужен ни механик, ни имеет ничего оБIIего с работой по вращеНИIО вала. Сборщики работаlОТ только в процес­ се создания автомобиля робот. Не бывает так, чтобы автомобиль собирали и вели одновременно, поэтому нет ну­ жды в механизме, который бы объединял в себе обе эти функции. Аналогично, сборка с IОЖНОГО составного объекта это такая работа, KOTOPYIO лучше отделить от обязанно­ стей, которые объект будет выполнять впоследствии, в ходе своего сущеСТВОВаНИЯ .

к иент, НрИБОДИТ к еще худшим последствиям. l(лиепт знает, KaKYIo работу нужно сдеНо Ilерекладывание ответственности на другую заинтересоваННУIО сторону, объект­

ГЛАВА 6. ЦИКЛ СУ ЩЕСТВОВАНИЯ ОБЪЕКТОВ МОДЕЛИ 133

лать, и поручает необходимые вычислительные операции объектам предметной области .

Если от клиента требуется самому собирать те объекты модели, которые для этого нуж­ ны, то он должен знать достаточно об их внутреннем устройстве. А чтобы соблюсти все инварианты, касаlощиеся взаимоотношений между отдельными частями объекта модели, клиент должен знать еще и некоторые внутренние правила (деловые регламенты) этого объекта. Даже простой вызов конструктора привязывает объект-клиент к конкретным классам объекта, который он создает. Никакие изменения в реализацию объектов модели теперь невозможно внести, не изменяя и объект-клиент, отчего становится труднее вы­ полнять рефакторинг .

Когда создание объекта модели перекладывается на объект-клиент, это приводит к лишним сложностям и делает менее четким распределение обязанностей. К тому же, об­ разуется брешь в инкапсуляции создаваемых объектов прикладной модели и АГРЕГАТОВ .

ти обязанностей с уровня предметной области. Тесная привязка операционного уровня к Что еще хуже, если клиент находится на операционном уровне, то происходит "утечка" час­ подробностям реализации модели сводит на нет большинство преимуществ абстрагирова­ ния па уровне предметной области и сильно усложняет внесение дальнейших изменений .

Создание объекта само п о себ е может быть очень важной операцией. Но сборка объекта - это совершенно не та работа, которую потом придется делать этому же объек­ ту в ходе своего существования. Объединение этих обязанносте й в одном о бъ екте по ­ рождает неуклюжие, трудные для п онимания пр ограммные конструкции. Если дать клиенту возможность упр авлять созданием о бъ ектов, это и скажает архитектуру само ­ го клиента, нарушает инкапсуляцию собранного объ екта или АГРЕГАТА, а также слиш­ ком сильно привязывает клиента к конкретной реализации создаваемого им объекта .

Создание сложных объектов - это обязанность уровня предметной области, но не объектов, которые непосредственно выражают модель. Бывают случаи, когда создание и сборка объекта соответствуют существенному событию в предметной области - например, "открытию банковского счета". Но сами по себе операции создания и сборки объекта обыч­ но не имеlОТ смысла в модели; это уже вопрос программной реализации. Чтобы решить эту проблему, приходится добавлять в модель конструкции, не ЯВЛЯlощиеся ни сущностями (ENTITIES), ни объектами-значениями (VALUE OBJECTS), ни службами (SERVICES). Здесь мы отходим от принципов предыдущей главы, поэтому важно подчеркнуть еще раз: мы добав­ ляем в модель элементы, которые ничему в ней непосредственно не cooTBeTcTBYIOT, но, тем не менее, ВЫПОЛНЯIОТ обязанности, относящиеся к уровню модели .

В любом объектно-ориентированном языке есть механизм создания объектов (например, конструкторы в Java и С++, класс создания экземпляров в Smalltalk). Но су­ ществует потребность и в более абстрактном механизме создания объектов, который не зависел бы от других об ъектов. Элемент программы, обязанность которого - создавать объекты, называется ФАБРИКОЙ (FACTORY) .

–  –  –

ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТI-IО-ОРИЕI-IТИРОВАННОГО .

. .

Так же, как интерфейс объекта должен инкапсулировать его реализацию, Т.е. давать возможность использовать операции объекта, не зная, как они устроены, ФАБРИКА ин­ капсулирует знания, необходимые для создания сложного объекта или АГРЕГАТА. Она предоставляет клиенту интерфейс, который соответствует его потребностям, и абстракт­ ное представление созданного объекта .

отдельному объекту, который сам по себе может не вьmолнятъ никаких функций в модели Передайте об язанности по созданию экземпляров сложных объектов и АГРЕГАТОВ предметной области, но, тем не менее, является элементом ее архитектуры. Обеспечьте IПI­ терфейс, который бы инкапсулировал все сложныIe операции сборки объекта и не требовал как единое целое, контролируя вып олнение инвариантов .

от клиеlПа ссылаться на конкретные классы создаваемого объекта. Создавайте АГРЕГАТЫ * ** ФАБРИКИ можно проектировать по-разному. В [ 14] подробно разобрано несколько специ­ альных архитектурных шаблонов для создания объектов - FACTORY METHOD (МЕТОД­

ФАБРИКА),

Abstract

FACTORY (АБСТРАКТНАЯ ФАБРИКА) и BUILDER (КОМПОНОВЩИК) .

Изложение в упомянутой книге касается в основном самых трудных вопросов создания объектов. Наша же цель - не углубиться в подробности проектирования ФАБРИК, а пока­ зать, что они занимаIОТ важное место в архитектуре предметной области. Правильное пользование ФАБРИКАМИ - залог успешного ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ (MODEL­ DRIVEN DESIGN ) .

Любая хорошая фабрика должна отвечать двум фундаментальным требованиям .

Каждый метод создания объекта должен быть един и неделим; он должен гаранти­ 1 .

ровать соБЛlодение всех инвариантов создаваемого объекта или АГРЕГАТА. ФАБРИКА должна уметь создавать только объект целиком в корректном состоянии. Для объ­ екта-СУЩНОСТИ это означает создание сразу целого АГРЕГАТА с соблюдением всех инвариантов; только необязательные второстепенные элементы разрешается доба­ вить позже. Для неизменяемого ОБЪЕКТА-ЗНАЧЕНИЯ это означает, что все атрибу­ ты инициализируются окончательными корректными значениями. Если интер­ фейс позволяет клиенту запросить создание некорректного объекта, то должна инициироваться исключительная ситуация или запуститься какой-то другой ме­ ханизм, не ПОЗВОЛЯIОЩИЙ возвратить некорректное значение .

Абстрагировать ФАБРИКУ следует к желаемому типу, а не к конкретному классу .

2 .

В этом могут помочь оригинальные шаблоны фабрик, предлагаемые в [ 1 4 ] .

Выб ор фабрик и их местонахождения Грубо говоря, ФАБРИКА вводится для того, чтобы создавать некие объекты, скрывая подробности, и помещается туда, где eIO будет удобно пользоваться. Принимаемые по этому поводу решения обычно касаются тех или иных АГРЕГАТОВ .

Например, если есть потребность добавлять элементы внутрь уже существующего АГРЕГАТА, можно создать МЕТОД-ФАБРИКУ в корневом объекте агрегата. Реализация внутренней структуры АГРЕГАТА таким образом скрывается от всех внешних клиентов, а поддержание целостности АГРЕГАТА при добавлении в него элементов поручается кор­ невому объекту, как показано далее на рис. 6. 13 .

средственно участвует в порождении другого объекта, хотя и не владеет им после созда­ А вот еще один пример. МЕТОД-ФАБРИКУ можно поместить в объект, который непо­ ния. В том случае, когда при создании объекта преимущественно используются данные и, возможно, деловые регламенты другого объекта, такой подход позволяет сэкономить

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАНИЯ ОБЪЕКТОВ МОДЕЛИ

на операции по извлечению информации из порождающего объекта с целыо послать ее куда-то для создания объекта. Также при этом отражаются особые отношения между по­

–  –  –

() \1 /

–  –  –

На рис. 6. 14 объект Торговая заявка (Trade Order) не входит в тот же агрегат, что и БрокерсКИЙ счет (Brokerage Account), ПОТОl\tlУ что дальше он будет взаимодейство­ вать с программой выполнения заявки, где Брокерский счет будет только MelIIaTb. Но даже с учетом этого кажется естественным дать Брокерскому счету право контроля над созданием Торговых заявок. БрокерсКИЙ счет содержит информацию, которая долж­ на помещаться в Торговую заявку (начиная с се собственных идентификационных дан­ ных), а также правила, которые определяют, какие заявки Я ВЛЯIОТСЯ правильными и разре­ может подвергнуться рефакторингу в иерархическую структуру, с отдельныl\t[и подкласса­ шенными. Полезно было бы также скрыть реализаЦИIО Торговой заявки. Например, она личие же ФАБРИКИ избавляет клиента от привязки К конкретным классам .

ми для Заявки на продажу (Виу Order) и Заявки на покупку (Sel l Order). На­

–  –  –

Рис. 6. 14. МЕТОД-ФАБРИКА nорождаеm СУЩIIОСТЬ, не входящую в тот же АГРЕГАТ ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТI-IО-ОРИЕНТИРОВАННОГО.. .

ФАБРИКА очень тесно связана со своими "изделиями", поэтому и помещать ее нужно только в объект, который имеет СИЛЬНУIО eCTecTBeHHYIO связь с порождаемым объектом .

Если HY)I(HO скрыть что-то - то ли KOHKpeTHYlo програММНУIО реализаЦИIО, то ли просто сложность процесса создания объекта, - но не находится естественного объекта для ин­ капсуляции, нужно создать специальную ФАБРИКУ или СЛУЖБУ. Автономная ФАБРИКА обычно порождает сразу целый АГРЕГАТ, выдавая ссылку на его корневой объект и кон­ тролируя соблюдение инвариантов созданного АГРЕГАТА. Если нужна ФАБРИКА для объ­ екта, внутреннего по отношению к некоторому агрегату, а корневой объект агрегата по каким-то причинам не подходит, смело создавайте для него отдеЛЫlУIО ФАБРИКУ. Однако при этом надо уважать правила доступа внутри АГРЕГАТА и следить, чтобы на порождае­ мый объект были возможны только временные ссылки извне этого АГРЕГАТА .

–  –  –

видел чересчур много программ, в которых все экземпляры объектов создавались К огда достаточно конструктор а Я вызовами конструкторов классов или подобного )I(e первичного механизма по­ прямыми рождения объектов, имевшегося в языке программирования. Применение ФАБРИК имеет большие преимущества, но, в целом, используется реже, чем хотелось бы. и все же время от времени приходится делать выбор в пользу конструктора именно за его прямоту, по­ тому что ФАБРИКИ могут усложнить работу с простыми объектами без полиморфизма .

Взвешивая "за" и "против", обычному общедоступному (public) конструктору нужно отдать предпочтение в слеДУIОЩИХ обстоятельствах .

Класс является типом. Он не входит ни в KaKYIO интересную иераРХИIО и не ис­ • пользуется полиморфически для реализации интерфейса .

Клиенту нужно знать реализаЦИIО объекта - возможно, с точки зрения выбора • СТРАТЕГИИ .

Все атрибуты объекта доступны клиенту, так что в конструкторе, предоставляе­ • мом клиенту, не создаются никакие новые объекты .

Создание объекта не является сложным процессом .

Общедоступный конструктор должен следовать тем же правилам, что и ФАБРИКА:

• это должна быть единая, неделимая операция, которая подходит для всех инвари­ антов создаваемого объекта .

Избегайте вызывать конструкторы в конструкторах других классов. Конструкторы должны быть проще простого. Сложную сборку, особенно АГРЕГАТОВ, поручите ФАБРИКАМ. И порог, за которым нужно вместо конструктора выбирать небольшой МЕТОД-ФАБРИКУ, совсем не высок .

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАI-IИЯ ОБЪЕКТОВ МОДЕЛИ 137 Б библиотеке классов ]ava имеются интересные примеры .

Бсе коллекции реализуют интерфейсы, которые отделяют клиента от деталей реализации. И все же они создаются прямыми вызовами конструкторов. А ФАБРИКА могла бы инкапсулировать иерархию коллекций. Методы ФАБРИКИ могли бы позволить клиенту запрашивать нужные ему свойства и возможности, а ФАБРИКА выбирала бы, экземпляр какого класса создать. То­ гда код для создания коллекций стал бы более выразительным, а новые классы­ коллекции можно было бы устанавливать, не нанося удар по всем программам Ha ]ava .

Но есть и случай, говорящий в пользу конкретных конструкторов. Бо-первых, выбор реализации может оказаться слишком затратным для многих программ, так что эти про­ граммы могут предпочесть делать его самостоятельно. (Хорошо построенная ФАБРИКА, тем не менее, могла бы и учесть такие факторы.) Б конце концов, классов-коллекций су­ ществует не очень много, и выбор сделать не так уж трудно .

Абстрактные типы коллекций сохраняют некоторую ценность, несмотря на отсутствие ФАБРИК, по причине особенностей их использования. Очень часто коллекции создаются в од­ ном месте, а используются в другом. Это означает, что клиент, который в конце концов поль­ зуется коллекцией, - добавляет, удаляет и извлекает содержимое - имеет право общаться только с интерфейсом и не зависеть от реализации. Обязанность выбора класса коллекции обычно падает на объект, владеющий коллекцией, или на ФАБРИКУ владеющего объекта .

Проектирование интерфейса Разрабатывая декларативный заголовок ФАБРИКИ (неважно, автономной или ФАБРИКИ­

–  –  –

димы для создания готового продукта-объекта, нужно передать за одну коммуни­ кационную операЦИIО с ФАБРИКОЙ. Придется также решить, что делать, если соз­ дание объекта сорвется, - например, в случае несоблюдения каких-то его инвари­ антов. Можно инициировать исключительную ситуацию или возвратить нулевое значение. Будьте последовательны и примите единый стандарт программирова­ НИЯ дЛЯ работы с ошибками создания объектов в ФАБРИКАХ .

Фабрика должна быть связана со своu.ми аргУМe1lтами. Если неосторожно выбрать на­ • бор входных параметров, можно создать целУIО паутину взаимосвязей. На степень за­ висимости ВЛИЯIОТ операции, которые выполняются над аргументом. Если он просто вставляется в создаваемый объект, эта зависимость невелика. Но если при конструи­ ровании объекта из аргумента берутся фрагменты, зависимость становится сильнее .

Самые безопасные параметры - это те, которые поступают с нижних уровней архи­ тектуры. Даже в пределах одного уровня существует тенденция разделения на подуров­ ни: более элементарные объекты используются более сложными. (Это деление на УРОВНИ будет рассматриваться с разных сторон в главе 10, а затем снова в главе 16.) Еще один хороший вариант параметра - это объект, который в модели является близ­ кородственным генерируемому, так что между ними не создается новая взаимосвязь­ зависимость. Б предыдущем при мере с объектом Позиция товарного заказа (Purchase Order I tem) метод-фабрика принимает в качестве аргумента объект Товар из катало ­ га (Catalog Part), который имеет естественную ассоциацию с Позицией ( I tem). Воз­ никает прямая взаимосвязь между классом ТоварНЪJЙ заказ (Purchase Order) и То­ варом (Part). Но эти три объекта и так образуют заМКНУТУIО концептуалЬНУIО группу .

АГРЕГАТ Товарного заказа уже и так ссьmался на Товар. Поэтому передача управления корневому объекту АГРЕГАТА и инкапсуляция внутренней структуры АГРЕГАТА это хоро­ ший выбор в данном случае .

ЧАСТЬ I I. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕI-IТИРОВАННОГО.. .

Используйте абстрактный тип аргументов, а не конкретные их классы. ФАБРИКА привязана к конкретному классу продуцируемых объектов; нет нужды привязывать ее еще и к конкретным параметрам .

Где реализовать ло гику инвариантов ?

Проследить, чтобы в создаваемом объекте или АГРЕГАТЕ выполнялись все инварианты ­ эта задача возложена на ФАБРИКУ. И все же нужно хорошенько подумать, прежде чем выно­ сить свойственные объекту регламентные правила за его пределы. ФАБРИКА может делегиро­ вать проверку инвариантов самому своему продукту, и чаще всего лучше так и делать .

Но у фабрик устанавливаются особые отношения с создаваемыми ими объектами .

Они уже знаlОТ внутреннюю структуру объектов, и сам смысл их существования в том и состоит, чтобы реализовать свой продукт. В некоторых обстоятельствах лучше как раз вынести логику инвариантов в ФАБРИКУ, не загромождая создаваемые объекты. Особен­ но удобно это делать с регламентными правилами АГРЕГАТОВ (которые распростраНЯIОТ­ ся на много объектов). А неудобно это делать с МЕТОДАМИ-ФАБРИКАМИ, которые ВКЛIО­ чены в другие объекты модели .

Хотя в принципе инварианты проверяются в конце ЛIобой операции, иногда разре­ IIIeHHbIe над объектом преобразования просто не позволяют сделать такую проверку. На­ пример, может существовать правило, реГУЛИРУЮIцее присвоение идентификационных данных объекту-сУЩНОСТИ. Но после создания объекта его данные могут оказаться не­ изменяемыми. Так, ОБЪЕКТЫ-ЗНАЧЕНИЯ целиком и полностыо неизменяемы. Незачем объекту тащить на себе реализацию логики, которая никогда не будет при меняться в те­ чение его активного существования. В таких случаях инварианты лучше реализовать в ФАБРИКЕ, не усложняя ее продукт .

Отличия м ежду фабрикам и сущн о стей и фабр иками о бъ ектов - значений ФАБРИКИ СУЩНОСТЕЙ и ФАБРИКИ ОБЪЕКТОВ-ЗНАЧЕНИЙ отличаются друг от друга двумя особенностями. ОБЪЕКТЫ-ЗНАЧЕНИЯ неизменяемы; продукт создается в оконча­ тельном виде. Поэтому операции ФАБРИКИ должны давать полное описание продукта .

ФАБРИКИ СУIЦIIОСТЕЙ же склонны работать только с самыми СУIlественными атрибу­ тами, необходимыми для создания корректного АГРЕГАТА. Детали можно добавить и поз)ке, если они не треБУIОТСЯ немедленно для соблюдения инварианта .

Есть еще проблемы, связанные с присвоением идентификационных данных СУ 11(­ насти - помимо ОБЪЕКТОВ-ЗНАЧЕНИЙ. Как говорилось в главе 5, идентификатор может назначаться программой автоматически или предоставляться снаружи оБыIноo пользова­ телем. Если индивидуальность покупателя контролируется по номеру телефона, этот но­ мер, очевидно, должен передаваться в виде аргумента в ФАБРИКУ. Соответственно, кон­ троль над процессом присвоения идентификатора программой удобно возложить на ФАБРИКУ. Хотя фактическое генерирование уникального идентификационного номера обычно выполняется процедурой базы данных или другим инфраструктурным механиз­ мом, ФАБРИКА знает, что именно запрашивать и куда это поместить .

Во с становление хранимых объектов в предыдущих разделах ФАБРИКА играла свою роль только в самом начале цикла су­ ществования объекта. В какой-то момент большинство объектов либо попадают в базу данных, либо передаются по сети. Притом очень немногие технологии баз данных сохра­ няют объектный характер своего содержимого - в большинстве способов передачи и

ГЛАВА 6. ЦИ КЛ СУЩЕСТВОВАНИЯ О БЪЕКТОВ М ОДЕЛИ

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

ФАБРИКА, используемая для восстановления объекта, очень похожа на ФАБРИКУ для его создания, если не считать двух основных отличий .

Фабрика, восстанавливающая объект-сущность, не nрисваивает ему новый иден­ 1 .

тификационный 'Номер. Если бы она это делала, то предыдущее воплощение объек­ та потерялось бы. Поэтому идентификационные атрибуты должны содержаться во входных параметрах ФАБРИКИ, занимающейся восстановлением объекта .

Фабрика, восстанавливающая объект, по-другому обрабатывает нарушение инварu­ 2 .

а'Нта. В ходе создания нового объекта фабрика просто сбрасывает его, если не удов­ летворяется инвариант, но при восстановлении требуется более гибкий подход. Если объект уже существует где-то в системе (например, в базе данных), этот факт нельзя просто проигнорировать. Но нельзя игнорировать и нарутпение регламентов. Должна существовать какая-то схема для разрешения таких противоречий, отчего восстанов­ ление становится более сложной задачей, чем создание новых объектов .

На рис. 6. 1 6 и 6. 1 7 показаны два способа восстановления. Некоторые из треБУIОЩИХСЯ для этого операций удобно реализованы в технологиях отображения объектов (object mapping), если речь идет о восстановлении из базы данных. Если же требуется более сложное восстановление из другого источника, то лучше воспользоваться специально предназначенной для этого ФАБРИКОЙ .

–  –  –

Рис. 6. 16. Восстановление объекта- сУЩНОСТИ, извлеченного из реляционной базы дaHHых циональные модули программы и явно определить их область действия. Это могут быть про­ Подведем итоги. Для создания экземпляров объектов следует тщательно подобрать функ­ сто конструкторы, но часто возникает и потребность в более абстрактных или сложных меха­ низмах создания экземпляров. Так в архитектуре программы появляется новый конструк­ тивный элемент под названием ФАБРИКА (FACTORY). Обычно ФАБРИКИ явно не выражают никакую функциональную часть модели, но, тем не менее, входят в ее архитектуру как эле­ менты, обеспечиваIощие четкую работу непосредственных смысловых объектов .

–  –  –

_Рис. 6. 1 7. ВОССl1zановленuе обьекrnо -СУIЦНОСТИ, переданного в формаl1zе XML Фабрика инкапсулируст те преобразования в цикле существования объектов, кото­ рые связаны с их созданием и восстановлением. Но есть еПJ;е и преобразование, пред­ сдача объектов на хранение и полученис их оттуда. Это преобразованис входит в обязан­ стаВЛЯIОlцее технические трудности и нстривиальное в архитсктурной реализации - это

–  –  –

Хранилища Благодаря наличию ассоциаций мо)кно найти один объект по его взаимосвязям с дру­ гими объектами. Но для этого HY)l(HO иметь отпраВПУIО точку, отталкиваясь от которой MO)I(HO проследить СУЩНОСТЬ или ОБ'ЬЕКТ -ЗНАЧЕНИЕ в ссредине их цикла существования .

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАНИЯ ОБЪЕКТОВ МОДЕЛИ

*** Чтобы делать с объектом что бы то ни было, нужно иметь ссылку на него. Как полу­ чить эту ссылку? Один из способов - создать объект, поскольку при создании объекта возвращается ссылка на него. Второй способ - проследить ассоциацию. Начинаем с объ­ екта, который нам уже известен, и запрашиваем у него информацию о связанном с ним объекте. Во всякой объектно-ориентированной программе это происходит постоянно .

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

Мне однажды попался проект, в котором разработчики, будучи энтузиастами ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ, пытались реализовать любые обращения к объектам че­ рез создание или прослеживание ассоциаций ! Их объекты находились в объектной базе данных, и они рассудили, что существующие концептуальные взаимоотношения между ними автоматически обеспечат нужные ассоциации. Достаточно, дескать, только провес­ ти подробный анализ и сделать всю модель предметной области связной. Это доброволь­ но наложенное на себя условие привело разработчиков к созданию как раз такого беско­ нечного переплетения связей, которого мы пытаемся избежать на протяжении вот уже нескольких глав, тщательно реализуя СУЩНОСТИ и подбирая АГРЕГАТЫ. Эта стратегия долго не продержалась, но разработчикам не удалось заменить ее каким-нибудь ДРУГИМ внятным и строгим подходом. В результате они нагромоздили сиюминутных решений и понизили планку своих амбиций .

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

Поиск по базе данных глобально доступен и позволяет непосредственно перейти к любому объекту. Нет никакой нужды в том, чтобы все объекты были взаимосвязаны достаточно урезать сеть взаимосвязей до приемлемого, управляемого состояния. Выбор между отслеживанием ассоциаций и поиском по базе становится рядовым проектным решением, в котором приходится балансировать между независимостью поиска и связ­ ностью ассоциаций. Должен ли объект Покупа-zaель (Cus tomer) хранить коллекцию всех своих Заказов (Orders)? Или же Заказы следует разыскивать в базе данных, ис­ пользуя поиск по полю Иден-zaифика-zaор покупа-zaеЛR (Customer ID)? Проектируя объекты, нужно выбирать разумное сочетание поиска и отслеживания ассоциаций .

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

С технической точки зрения извлечение хранимого объекта ЭТО частный случай операции его создания, поскольку данные из базы используются фактически для сборки быть об ЭТОЙ суровой реальности. Но с концептуальной точки зрения это всего лишь се­ нового объекта. Действительно, код, который приходится для этого писать, не дает за­ редина цикла существования объекта-сУЩНОСТИ. Ведь объект Покупа-zaель (Cus tomer) не стал представлять нового покупателя только потому, что объект положили в базу дан­ ных, а затем достали из нее. Чтобы не забывать об этом различии, мы и говорим о созда­ нии нового экземпляра на основе сохраненных данных как о восстановлении объекта .

Цель DDD состоит в том, чтобы научиться писать более качественные программы, со­ средоточившись на модели предметной области, а не на программных технологиях. Пока 1 42 ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

разработчик построит запрос SQL, передаст его в службу обработки запросов на ин фра­ ИУIО информацию и передаст ее в конструктор или ФАБРИКУ, от его концентрации на моде­ структурном уровне, получит набор строк из таблицы базы данных, извлечет И3 них нуж­ ли ничего не останется. Так вырабатывается способ мышления об объектах всего лишь как о контейнерах данных, извлекаемых по запросам, и вся архитектура программы начинает ориентироваться на задачи обработки данных. Технические детали могут различаться, но главная проблема остается: клиентская часть взаимодействует с технологиями, а не с поня­ тиями модели. Здесь могут оказаться полезными такие инфраструктуры, как УРОВНИ ОТОБРАЖЕНИЯ МЕТАдАННЫХ (METADATA MAPPING LAYERS) [ 1 3]. с их помощью облегчает­ ся преобразование результатов запросов в объекты, но и в этом случае программист по­ прежнему думает о технических механизмах, а не о предметной области. Что еще хуже, если клиентский код напрямую обраIцается к базе данных, у программистов возникает искушение вообще выбросить такие конструкции модели, как АГРЕГАТЫ, или даже инкапсуляцию объек­ тов, и заняться прямой обработкой извлекаемых данных. Регламентные правила предметной области все больше перетекают в код запросов к баэе данных, а то и попросту отбрасываются .

то все равно носят механистический характер, и разработчиков продолжают соблазнять пер­ Объектные базы данных, конечно, снимают проблему преобразования, но механизмы поиска­ спектива "вытаскивать" из базы любые объекты по своему усмотрению .

Клиентское приложение нуждается в удобных средствах получения ссылок на суще­ ствующие объекты предметной области. Если инфраструктура позволяет это делать от­ ассоциаций, загромождая ее. С друrой стороны, они MOryT С помощью запросов изме­ носительно леrко, разработчики клиента добавляют в модель больше прослеживаемых кать из базы данных в точности ту информацию, которая им нужна - например, достать несколько конкретных подобъектов, не обращаясь к корневому объекту АГРЕГАТА. Та­ ким образом операционная лоrика предметной области переносится в запросы и клиент­ ский код, а роль СУЩНОСТЕЙ и ОБЪЕКТОВ-ЗНАЧЕНИЙ сводится к простым контейнерам данных. Техническая сложность реализации всей инфраструктуры доступа к базе дан­ ных быстро загромождает код клиента, вынуждая разработчиков урезать и упрощать уровень предметной области. В итоrе модель становится бесполезной .

Если придерживаться изученных нами до сих пор принципов проектирования, можно попробовать несколько сузить рамки проблемы доступа к объектам. Вот бы иметь такой метод доступа, который позволил бы не отступить от этих принципов И сохранить имен­ но модель в центре внимания при разработке программы. Для начала не следует беспо­ коиться о временных объектах. Такие объекты (обычно ОБЪЕКТЫ-ЗНАЧЕНИЯ) прожива­ IOT короткую жизнь - они используются В операциях создавших их клиентов, а затем зовать запросы к базе данных, если их удобнее находить по цепочке ассоциаций. Напри­ сбрасываются. Что касается постоянных объектов, то для работы с ними не надо исполь­ мер, адрес человека можно узнать И3 объекта Человек (Person). И что самое важное, ся только через корневой объект агрегата .

обращение к любому объекту, внутреннему по отношению к АГРЕГАТУ, может выполнять­ Постоянные ОБЪЕКТЫ-ЗНАЧЕНИЯ обычно находятся путем отслеживания ассоциаций от какой-нибудь сущности, слу)каIцей корневым объектом для ИI-Iкапсулирующего их АГРЕГАТА. Фактически доступ к ЗНАЧЕI IИIО через глобальный поиск часто не имеет смысла, поскольку нахождение ОБЪЕКТА-ЗНАЧЕНИЯ по его свойствам эквивалентно С03данию нового экземпляра с теми же свойствами .

Впрочем, бывают и исключения. Например, если я планирую поездку с помощью транспортной онлайн-системы, то иногда сохраняю несколько потенциальных маршру­ тов, а поз)ке возвращаюсь и выбираю один И3 них для резервирования билетов. Эти мар­ шруты представляют собой ЗНАЧЕНИЯ (если бы существовало два маршрута, состоящих

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАНИЯ ОБЪЕКТОВ МОДЕЛИ

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

Надо сказать, что глобальный доступ к ОБЪЕКТАМ-ЗНАЧЕНИЯМ распространен значи­ тельно меньше, чем к сущностям. Так что если у вас возникает необходимость искать в базе данных существующий ОБ'ЬЕКТ-ЗНАЧЕНИЕ, стоит подумать над тем, не представляет ли он на самом деле СУIl{НОСТЬ, индивидуальность которой вы еlде просто не осознали .

Из вышеизложенного должно быть ясно, что большинство объектов не требует гло­ бального поиска для обращения к себе. А те, которые треБУIОТ, нужно соответствующим образом представить в архитектуре программы .

Теперь проблему можно сформулировать более точно .

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

турой, а иногда значения из перечислимых типов. Предоставление такого доступа к Обычно это СУЩНОСТИ, иногда ОБЪЕКТЫ-ЗНАЧЕНИЯ со сложной внутренней струк­ другим о бъектам стирает основные различия между разновидностями объектов. ОТ­ сутствие ограничений н а запросы к базе данных может нарушить инкапсуляцию объ ­ ектов и АГРЕГАТОВ предметно й области. Открытое использование технической инфра­ структуры и механизмов доступа к базам данных усложняет работу клиентскоrо при­ ложения и знаменует отход от принципов П РОЕКТИРОВАНИЯ ПО МОДЕЛИ .

Имеется множество приемов для решения технических проблем доступа к базам дан­ ных. Можно, например, инкапсулировать код SQL в ОБЪЕКТЫ-ЗАПРОСЫ (QUERY OBJECTS) или выполнять перевод из объектов в таблицы и назад через УРОВI-IИ ОТОБРАЖЕНИЯ МЕТАДАННЫХ [ 1 3]. Восстановление храНЯIДИХСЯ объектов мо)кно выполнять через ФАБРИКИ (об этом еще будет говориться). Эти и многие другие приемы дают ВОЗМО)l(НОСТЬ абстрагироваться от технических сложностей и тонкостей .

А теперь следует еще раз напомнить, что мы потеряли. Мы уже не думаем о понятиях пашей модели предметной области ! Код уже не передает предметную суть выполняемых операций, а занимается пересылкой и обработкой данных. Шаблон ХРАНИЛИЩА (REPOSITORY) это концептуально несложная архитектура, позволяющая инкапсулиро­ вать описанные выше технические решения и сконцентрировать внимание на приклад­ ной модели .

ХРАНИЛИЩЕ представляет все объекты определенного типа в виде концептуального множества (обычно виртуального, эмулируемого). Оно работает аналогично коллекции, только с более развитым механизмом запросов. Можно добавлять и удалять объекты со­ отвеТСТВУЮlдего типа, и скрытые механизмы ХРАНИЛИIЦА будут автоматически поме­ щать их в базу данных или удалять из нее. Введя это определение, получаем полный на­ бор средств для доступа к корневым объектам АГРЕГАТОВ с самого начала и до конца их цикла существования .

Клиенты запрашивают объекты из ХРАНИЛИIЦА, используя запросные методы (query methods), которые отбирают объекты по критериям, задаваемым клиентами, - обычно по значениям определенных атрибутов. ХРАНИЛИЩЕ выдает запрашиваемый объект, ИН­ капсулируя механизмы запросов к базе данных и отобра)l(ения метаданных. ХРАНИЛИЩА могут реализовать множество самых разных запросов, отбирая объекты в зависимости от установленных клиентами критериев. Они также могут возвращать информацию свод­ ного характера, - например, сколько экземпляров объектов удовлетворяют тому или иному критерию. ХРАНИЛИЩЕ может даже выполнять вычисления по итогам запроса, 144 ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

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

–  –  –

Наличие ХРАНИЛИЩА снимает с клиента огромную тяжесть -- теперь он мо)кет об­ щаться с простым, скрываЮlIИМ технические подробности интерфейсом, и запрашивать нужную ему информацию в терминах модели. Для поддержки всего этого нужна разви­ тая и сложная техническая инфраструктура, но сам интерфейс остается простым и кон­ цептуально привязанным к модели предметной области .

Для каждого пmа объектов, к которым требуется глобальный доступ, введите объект­ в коллекцию и находятся в оперативной памяти. Наладьте доступ через х орошо извест­ посредник, который может создать иллюзию, что все объекты такого типа объ единены ный глоб альный интерф ейс. Реализуйте методы для доб авления и удаления объектов, инкапсулирующие реальное помещение информации в базу данных и удаление ее от ­ туда. Реализуйте методы, которые будут выбирать объекты по заданным критериям и возвращать полностью сгенерированные и инициализированные объекты или кол­ лекции объ ектов с атрибутами, подходящими под критерии, таким образом инкапсу­ лируя реальные технологии хранения данных и выполнения запросов. Реализуйт е ХРАНИЛИЩА только для тех АГРЕГАТОВ, к корневым объектам которых треб уется пря­ мой доступ. Программа-клиент должна опираться на модель, а все операции хранения

–  –  –

ХРАНИЛИЩ есть ряд важных преимуществ, ведь они:

у предоставляют клиентам простую модель для получения устойчиво существую­ • щих объектов и управления их жизненным циклом;

убирают из операционной части приложения и модели предметной области необ­ • ходимость в технической поддержке целостности объектов, разных вариантов технологий СУБД, и да)l(е разных источников данных;

выражают в себе проектные решения по способам доступа к объектам;

• позволяют легко заменить себя "заглушками" для целей тестирования (обычно за­ • глушкой служит наХОДЯlIаяся в оперативной памяти коллекция) .

Запросы к хранилищам Все ХРАНИЛИЩА должны содержать методы, с помощью которых клиенты могут за­ прашивать объекты, соответствующие некоторым критериям. Но вот в организации та­ кого интерфейса возможны варианты .

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАНИЯ ОБЪЕКТОВ МОДЕЛИ

Самое простое в разработке ХРАНИЛИЩЕ содержит явно прописанные в коде запросы с конкретными параметрами. Предназначение запросов может быть самым разнообраз­ ным: извлечение СУЩНОСТИ по ее идентификационным данным (это реализовано прак­ тически в любом ХРАНИЛИЩЕ); получение коллекции объектов по значению какого-либо атрибута или сложной комбинации параметров; отбор объектов по диапазону значений атрибутов (например, по датам); и даже различные расчеты, не выходящие за пределы общей компетенции ХРАНИЛИЩ (в виде интерфейса к операциям используемой СУБД) .

Большинство таких запросов возвращает объект или коллеКЦИIО объектов, но в рамки концепции вполне вписывается и возврат результатов различных сводно-статистических вычислений, - например, количества объектов или суммы значений тех числовых атри­ бутов, которые в модели задействованы именно в такой операции .

–  –  –

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

А вот в проектах с большим количеством запросов можно попытаться построить не просто ХРАНИЛИЩЕ, а целую архитектурную среду, ассоциированную с ним, в которой можно было бы составлять более гибкие запросы. Для этого потреБУIОТСЯ кадры, знако­ мые с нужными технологиями, а также соответствующая техническая инфраструктура .

Одно из особенно удачных решений для обобщения ХРАНИЛИЩ путем создания ар­ хитектурной среды состоит в том, чтобы строить запросы на основе СПЕЦИФИКАЦИЙ (SPECIFICATION). СПЕЦИФИКАЦИЯ позволяет клиенту описывать (т.е. задавать, специфи­ цировать), что именно ему нужно, и при этом не беспокоиться, как именно это делается .

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

Этот архитектурный шаблон будет рассматриваться подробно в главе 9 .

–  –  –

Рис. 6.20. Гибкая декларативная СПЕЦИФИКАЦИЯ критериев поиска в сложном ХРАНИЛИЩЕ с расши­ ренны.ми возможностями Запросы на основе СПЕЦИФИКАЦИЙ составляются гибко и удобно. В зависимости от имеющейся инфраструктуры, архитектурная среда для хранилищ может быть или со­ всем простой, или непомерно усложненной. Проектирование таких хранилищ и связанЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ П РЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

ним техническую проблематику подробнее рассматривают Роб Ми (Rob Мее) HYIO С Эдвард Хайет (Edward Hieatt) в [ 1 3] .

и Даже если хранилище спроектировано на выполнение гибких запросов, оно должно позволять также и добавлять специализированные, явно прописанные запросы. Это мо­ гут быть вспомогательные методы, инкапсулирующие часто используемые запросы, или такие, которые возвращают не сами объекты, а, например, результаты определенных ма­ тематических операций с ними. Среды, которые не предусматривают такую возмож­ ность, в конце концов, либо "замутняют" чистоту архитектуры предметной области, либо просто игнорируются разработчиками .

Клиентам б е зразлична реализ ация хранилищ, а разрабо тчикам - нет Реализация технологии длительного хранения и поддержания целостности объектов дает возможность приложению-клиенту быть очень простым и совершенно независимым от конкретной реализации ХРАНИЛИЩА. Но инкапсуляция инкапсуляцией, а часто быва­ ет, что разработчикам необходимо знать, что происходит там внутри, "в глубине" .

ХРАНИЛИЩА могут работать и использоваться очень по-разному, и вопросы быстродей­ ствия часто играют ключевую роль .

Кайл Браун ( Kyle Brown) рассказал мне историю о том, как к нему обратились для ре­ шения проблем с системой управления производством на основе WebSphere, которую он как раз устанавливали в ее рабочей среде. Система загадочно съедала всю память после не­ скольких часов работы. Кайл просмотрел код и нашел причину. В какой-то момент в сис­ теме собиралась сводная информация по всем учитываемым объектам на предприятии .

Разработчики реализовали это в виде запроса под названием "все объекты", который созда­ вал и инициализировал экземпляры всех объектов, а потом отбирал то, что было нужно .

Получалось так, как будто вся база данных одним махом оказывалась в памяти! При тести­ ровании этой проблемы не было, потому что объем тестовых данных был невелик .

Здесь недосмотр очевиден, но серьезные проблемы могут возникнуть и из-за гораздо более мелких недочетов. Разработчикам необходимо знать технические характеристики операций, инкапсулированных в объектах, - пусть и не обязательно мельчайшие под­ робности их реализации. Если компонент хорошо спроектирован, ему можно дать такую характеристику. (Это одна из главных тем в главе 10.) Как говорилось в главе 5, на выбор тех или иных решений в моделировании могут по­ влиять особенности и ограничения инфраструктурной технологии. Например, наличие реляционной базы данных накладывает практические ограничения на глубокие сложно­ составные объектные структуры. В общем, разработчики должны иметь достаточный доступ к потоку информации с обоих уровней: как по вопросам использования ХРАНИЛИЩА, так и по технической реализации его запросов .

Реализ ация хранилища Конкретные реализации ХРАНИЛИЩ могут сильно отличаться друг от друга в зависи­ мости от имеющейся инфраструктуры и технологии контроля существования объектов .

В идеале было бы хорошо спрятать от приложения-клиента (но не от разработчика этого клиента) все детали операций, чтобы код клиента оставался одним и тем же независимо от того, хранятся ли данные в объектной базе, реляционной базе или просто в оператив­ ной памяти. ХРАНИЛИЩЕ же будет делегировать задания соответствующим службам инфраструктуры для выполнения порученной ему работы. Инкапсуляция механизмов

ГЛАВА 6. ЦИКЛ СУЩЕСТВОВАНИЯ ОБЪЕКТОВ МОДЕЛИ

хранения данных, их извлечения и выполнения запросов - это самое основное в реали­ зации ХРАНИЛИIЦА .

Концепцию ХРАНИЛИIЦА можно адаптировать ко многим ситуациям. Возможности ее реализации настолько разнообразны, что здесь будет приведено только несколько общих принципов, которые полезно помнить .

Абстрагируйте тип. ХРАI-IИЛИIЦЕ как бы "содержит в себе" все экземпляры опре­ • деленного типа, но это не значит, что для ка)кдого класса надо иметь свое храни­ лище. В качестве типа можно использовать абстрактный падкласс из иерархии .

Например, Товарная заявка (TradeOrder) может БыIьь как Заявкой на по ­ купку (BuyOrder), так и Заявкой на продажу ( S e l l 0rder). Тип также может нредставлять собой интерфейс, реализаторыl которого да)ке не связаны иерархиче­ ски. Но это может быть и один конкретный класс. IIe забывайте, что всегда мож:но встретить препятствия на пути реализации всего ЭТОl о из-за отсутствия подобного из полиморфизма в технологии СУБД .

Извлекайте преимущества независимости от клиента. Реализация ХРАIIИЛИlЦА • предоставляет значительно больше свободы для внесения изменений, чем тот слу­ чай, когда клиент вызывает механизмы наПрЯМУIО. Этим можно воспользоваться для оптимизации быстродействия, варьируя запросы или кэшируя объекты в памяти, по )келанию меняя общую методику хранения и поддержания целостности объектов .

Можно также облегчить тестирование клиентского кода и объектов модели пред­ метной области, построив легко управляемую СИМУЛЯЦИIО ХРАI [ИЛИIЦА с хранением объектов в оперативной памяти .

–  –  –

Рис. 6.2 1. Инкапсуляция реальной системы хранения данных в обьеКl1zе-ХРАНИЛИЩЕ Оставьте контроль транзакций клиенту. Хотя именно ХРАI-IИЛИIЦЕ помещает • данные в базу и извлекает их оттуда, оно, как правило, не должно контролировать их завершение (т.е. выполнять фиксацию транзакции). Конечно, есть искушение, например, зафиксировать транзакцию после сохранения данных, но у клиента наЧ АСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

верняка есть собственный контекст для корректной инициализации и завершения отдельных рабочих операций. Контроль транзакций со стороны клиента значи­ тельно облегчается, если ХРАНИЛИЩЕ в это дело не вмешивается .

Обычно разработчики размещают архитектурную среду для поддержки ХРАНИЛИЩ на инфраструктурном уровне. Кроме обеспечения связи с компонентами инфраструкту­ ры, которые расположены ниже, надкласс ХРАНИЛИЩА может также реализовать неко­ торые важнейшие запросы, особенно когда имеется механизм гибкого их построения .

К сожалению, в такой системе типов, как у Java, приходится типизировать возвращаемые объекты, как "Объекты", оставляя клиенту работу по приведению их к объявленному ти­ пу ХРАНИЛИЩА. Но это, конечно, приходится делать с запросами, которые в Java и так возвращают коллекции .

Некоторые дополнительные рекомендации по программированию хранилищ и тех­ нические шаблоны для их поддержки (такие, как "объект-запрос", query object) можно найти в [ 1 3 ] .

Раб ота в рамках архитектурной среды Прежде чем программировать что-нибудь вроде ХРАНИЛИЩА, необходимо хорошень­ ко обдумать инфраструктуру, с которой приходится работать, - особенно архитектур­ ную среду, если она есть. Можно обнаружить, что среда предоставляет такие возможно­ сти, с ПОМОIЦЬЮ которых легко построить ХРАНИЛИЩЕ, а бывает и так, что она только мешает, и чем дальше, тем больше. Может оказаться, что в архитектурной среде уже оп­ ределены адекватные шаблоны для поддержания существования объектов, но иногда по­ лучается так, что готовые шаблоны вообще ничем не похожи на ХРАНИЛИЩА .

Пусть, например, проект строится на основе J2EE. Если поискать концептуальное сходство между этой средой и ПРОЕКТИРОВАНИЕМ ПО МОДЕЛИ (и при этом помнить, что объект Java Веап и объект-сУЩНОСТЬ - не одно и то )ке2 ), то можно, например, волевым решением назначить объекты Java Веап корневыми объектами АГРЕГАТОВ. Конструкция Ноте. Попытка выдать ее за ХРАНИЛИЩЕ может вызвать и другие проблемы .

в архитектурной среде J2EE, обеспечиваЮllая доступ к таким объектам, называется E JB В целом, можно посоветовать "не плыть против течения" архитектурной среды. Пы­ тайтесь придерживаться принципов DDD и при этом не отвлекаться на частности, когда среда работает против вас. Иllите сходство между концепциями предметно-ориенти­ рованного проектирования и принципами устройства среды, в которой работаете. Все это, конечно, справедливо в предположении, что вы не имеете права уклониться от рабо­ ты со средой. Во многих проектах на основе J2EE объекты Java Веап вообllе не исполь­ зуются. Если у вас есть свобода выбора, работайте с теми средами или их фрагментами, которые согласуются с принятым вами архитектурным стилем .

Связь с ф абр иками с ним в середине и конце его жизни. Если объекты находятся в оперативной памяти или ФАБРИКА ведает началом существования объекта, а ХРАНИЛИIДЕ помогает работать хранятся в объектной базе данных, то тут все просто. Но обычно хотя бы часть данных программы сохраняется в реляционной базе, файле и других необъектных системах .

В таких случаях извлеченные из этих мест хранения данные приходится восстанавливать в объектную форму .

2 Т.е. соответственно, entity Ьеаn и просто entity. Против этой путаницы и предостерегает автор. ­ Прuмеч. перев .

–  –  –

оно и есть. Но все-таки полезно на первом плане держать концептуалЬНУIО модель, а с ее по­ зиций, как уже говорилось, восстановление хранимого объекта не есть создание нового .

В методике проектирования, основанной на предметной области, ФАБРИКИ и ХРАНИЛИЩА выполняют разные функции. ФАБРИКА создает новые объекты; ХРАНИЛИIЦЕ находит и из­ влекает старые. ХРАНИЛИЩЕ должно давать клиентам иллюзию, что объекты хранятся прямо в памяти. Бывает, что объекты приходится восстанавливать (да, и при этом созда­ вать новые экземпляры), но концептуально это те же самые объекты, которые уже супест­ вовали - это просто середина их жизненного цикла .

Ч тобы примирить разные точки зрения, достаточно сделать так, чтобы ХРАНИЛИЩЕ делегировало создание объектов ФАБРИКЕ, которая также (теоретически, хотя на практи­ ке и редко) могла бы создавать и совсем новые объекты "с нуля" .

Четкое разделение этих обязанностей помогает также снять с ФАБРИКИ всякую от­ ветственность за поддержание целостности (непрерывности существования) объекта .

Работа ФАБРИКИ создать объект любой требуемой сложности на основе данных. Если в результате получается новый объект, об этом должен знать клиент, который при жела­ нии может добавить его в ХРАНИЛИЩЕ, а оно уже инкапсулирует операции по сохране­ нию объекта в базе данных .

–  –  –

ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО .

. .

Искушение объединить ФАБРИКУ и ХРАНИЛИЩЕ появляется еще в одном случае - при желании реализовать функцию "поиска или создания". При этом клиент описывает, какой объект ему нужен, и если поиск показывает, что такого объекта еще не существует, то он соз­ дается и предоставляется клиенту. Подобных функций следует избегать. В лучшем случае ее наличие создает совсем небольшие удобства, но даже кажущаяся ее полезность исчезает вовсе, если в программе делается различие между СУЩНОСТЯМИ и ОБЪЕКТАМИ-ЗНАЧЕНИЯМИ. Если клиенту нужен ОБЪЕКТ-ЗНАЧЕНИЕ, он обращается к фабрике и получает новый. Как правило, различие между новым и уже существующим объектами играет важную роль в предметной области, и если средства архитектурной среды позволяют сымитировать отсутствие такого различия, то на самом деле они только запутывают дело .

Про екти р ов ание о бъ ектов для реляци онн ой базы данных Самой распространенной необъектной компонентой программных систем, которые в основном следуют объектно-ориентированному подходу, является реляционная база данных. Ее наличие порождает проблемы смешения парадигм (см. главу 5). Но база дан­ ных более тесно связана с объектной моделью, чем большинство прочих компонентов .

База данных не просто имеет дело с объектами - она хранит в себе постоянную форму тех данных, которые образуют эти самые объекты. Уже немало написано о технических трудностях и особенностях проекции (отображения) объектов на реляционные базы данных, эффективного их хранения и извлечения. В частности, этот вопрос рассматрива­ ется в книге [ 1 3]. Существуют достаточно отлаженные средства для построения соответ­ ствий между этими двумя формами данных и управления ими. Не считая технических трудностей, некорректное построение такого соответствия может иметь существенное влияние и на саму объектную модель .

Наиболее распространены три случая .

База данных является в основном хранилищем объектов .

1 .

База данных была разработана для другой системы .

2 .

База данных разработана для этой системы, но выступает в роли, отличной от хра­ 3 .

нилища объектов .

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

Средства отображения данных сами по себе достаточно богаты возможностями, чтобы сгла­ дить любые существенные отличия. Проблема в том, что иметь много накладывающихся друг на друга моделей не очень-то удобно и слишком сложно. К этому случаIО применима та реко­ мендация, которую мы упоминали в разговоре о преимуществах ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ избегать разделения двух процессов, анализа проблемы и проектирования модели .

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

В зависимости от возможностей имеющихся средств отображения данных, возможно агреги­ рование и комбинирование объектов. Но очень важно, чтобы отображение сохраняло про

–  –  –

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

Процессы, протекающие вне объектной системы, вообlце не должны иметь доступа • к храНИЛИIЦУ объектов, потому что они могут нарушить наЮlадываемые объектами ин­ вариантные ограничения. Кроме того, предоставление им права доступа заблокирует модель данных от изменений, и это еще скажется, КОlда придет время рефакторинга .

С другой стороны, во многих случаях данные поступаJОТ из устаревшей или внешней системы, которая никогда и не задумывалась как хранилище объектов. В такой ситуации в одной системе фактически соседствуют две модели предметной области. Этот вопрос подробно разбирается в главе 14. Иногда имеет смысл приспособиться к модели, приня­ той в другой системе, а иногда, наоборот, - сделать CBOIO модель совершенно другой .

Еще одна причина, по которой приходится делать ИСКJпочения -- это быстродействие. Для решения проблем в этой области программисту приходится идти па многие ухищрения .

Но для важного и распространенного случая, когда реляционная база данных служит постоянным хранилищем объектов из объектно-ориентированной предметной области, лучше всего применять самый прямой подход. Строка таблицы должна содержать объект возможно, вместе с его подобъектами в виде АГРЕГАТА. Внешний ключ в таблице должен транслироваться в ссылку на другой объеКТ-СУIЦНОСТЬ. Если и встречается необходи­ мость иногда отойти от JTOrO прямого подхода, это не должно приводить к 1I0ЛНОМУ заб­ вению принципа прямого соответствия .

Привязать объектную и реляционную состаВЛЯЮUJ:УЮ к одной и той же модели помо­ ветствовать именам и ассоциациям в реляционных таблицах. Хотя в присутствии мощ­ гает ЕДvIl lыtI Я З Ы К. Имена и ассоциации элементов в объектах должны до мелочей соот­ ных средств отображения данных это может показаться несущественным, даже неболь­ шие различия в отношениях между данными могут вызвать БОЛЫНУIО путаницу .

Традиция рефаКТОРИIlга, которая все больше овладевает объектно-ориентированным миром, пока не слишком сильно повлияла на нроектировапие реляционных баз данных .

Более того, серьезные проблемы пере носа данных делаIОТ частые изменения нежелатель­ ными. Это может затормозить рефакторинг объектной м одели, но если модель базы дан­ ных и объектная модель начинают расходиться, то может быстро потеряться прозрач­ ность, наглядность преобразования данных .

Наконец, могут быть и причины для введения такой структуры базы данных, которая решительно отличается от объектной модели, пусть даже база специально создавалась именно для данной программной системы. База данных может также использоваться другой программой, в которой вообще не инициал изируются дкземпляры объектов. Та­ кая база может практически не требовать изменений даже тогда, когда поведение объек­ тов быстро меняется. Тогда возникает искушение углубить разрыв между системой и ба­ зой данных. Часто это делается непреднамеренно - разработчикам просто не удается вести базу данных "в ногу" с моделью. Если же такой разрыв выбирается сознательно, в результате вполне может получиться аккуратная и экономная структура таблиц базы, а не корявое нечто, порожденное многочисленными попытками привязать базу данных к самой последней версии объектной модели .

–  –  –

П редыдуrцие три главы БыIии посвящены введению в язык шаблонов, пригодный для отшлифовки мельчаЙI11ИХ деталей моделей и поддержки строгой методики ПРОЕКТИРОБАНИЯ ПО МОДЕЛИ ( MODEL-DRIVEN DESIGN). Б приведенных ранее примерах архитектурные шаблоны большей частыо применялись по одному за раз, но в реальном проекте их придется использовать одновременно. В этой главе представлен реальный проект). В ЭТОlVI примере l\lbI пройдем по цепочке последовательных усовер­ один БОЛЫIIОЙ и подробный при мер (хотя, конечно, намного более простой, чем ЛIобой шенствований модели и архитектуры программы точно так же, как гипотетическая груп­ па разработчиков могла бы реIпать проблемы программной реализации и удовлетворять требования заказчика, попутно выстраивая архитектурный проект на основе модели предметной области. При этом будет показано, какие факторы действуют в том или ином случае и как шаблоны из части 11 помогаIОТ с ними справляться .

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

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

2. ОфорrvlЛЯТЬ заказ заранее .

з. Автоматически высылать клиенту счет-фактуру по достижении грузом некоторого операционного пункта Maplll pYTa .

В реальном проекте пришлось бы потратить немало времени и итераций на то, чтобы прийти к простой и ясной модели. Подробно этот исследовательский процесс будет рассмот­ и естественной (l)opMe, а потом сосредоточимся на проработке деталей архитектуры .

рен в части 111. А здесь мы начнем с модели, которая выражает нужные понятия в разумной Эта модель служит для организации знания из предметной области и предоставляет раэработчикам рабочий язык. Теперь можно сq)ормулировать такие утверждения .

В работе с Груз ом (Cargo) участвует uесколько Клиентов (Cu s t omers), каJlсдый из KOmOPlJlX играет свою роль (lvle) .

Должна задаваться (Ье specijied) цель ({!;oal) доставки груза .

Цель (goal) доставки груза достигается в результате последовательности Переездов (Carri er Movement), которые удовлemвор.яl0т Заданию (Speci fi ca t i on) 1 .

–  –  –

Рис. 7. 1. Диаграмма классов, nредставляющая модель обслуживания доставки грузов Каждый объект в этой модели имеет четкий смысл .

Манипуляция (Handl ing Event) - это дискретное действие, выполняемое над Грузок (Cargo), например, погрузка его на судно или "проведение" через таможню .

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

Задание на доставку (Del ivery Speci fi cation) определяет цель доставки гру­ за - это как минимум пункт следования и дата прибытия, но могут быть и дополнительные данные. Этот класс следует шаблону SPECIFICATION (СПЕЦИФИКАЦИЯ) (см. главу 9) .

Эти обязанности можно было бы возложить на объект Груз, но абстракция в виде Задания на доставку дает как минимум три преимущества .

1. Если бы не было Задания, объект Груз отвечал бы за множество детальных атри­ бутов и ассоциаций, задаIОЩИХ цели доставки груза. Это загромоздило бы Груз и сделало бы его трудным для понимания и изменения .

2. При объяснении модели в целом такая абстракция позволяет легко и безопасно скрывать подробности. Например, в Задании могут быть инкапсулированы и дру­ гие критерии, но на общих схемах модели их рисовать не обязательно. Схема со­ общает читателю, что на доставку имеется Задание, а подробности его в данный момент неважны (и могут быть легко изменены позже) .

3. Такая модель более выразительна. Добавлением в нее Задания на доставку мы как бы говорим, что определяются не точные средства доставки Груза, а конечная цель, которая преследуется этой доставкой .

ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛ ЕМЕНТЫ П РЕДМЕТI-I О -ОРИЕI-IТИРОБАННОГО .

. .

Роль предназначена для того, чтобы разделять обязанности, исполняемые тем или иным Клиентом в процессе доставки груза. Один из них будет "отправителем", другой ­ " получателем", третий - "плательщиком" и т.п. Для конкретного Груза только один Кnиент может выступать в той ИЛИ иной роли, поэтому ассоциация меняет свой тип с "многие ко мно­ гим" на "многие к одному". Роль можно реализовать просто как текстовую строку, хотя если от нее понадобится более сложное поведение, ее можно сделать и классом .

Переезд представляет одно перемещение конкретного Транспортного средства (Carrier), такого как судно или грузовик, из одного Местоположения (Location) в другое. Грузы переезжают из одного места в другое благодаря тому, что их грузят на транспортныe средства на время одного или нескольких Переездов .

ИСТОРИЯ доставки (Del ivery His tory) описывает, что в действительности про­ исходило с Грузом в отличие от Задания на доставку, которое описывает желае­ мое. Объект История доставки может вычислить текущее Местоположение нужного Груза, проанализировав послеДНIОIО погрузку или разгрузку, а также пункт назначения соответствующего Переезда. Успешная доставка заканчивается тем, что объект Исто ­ рия доставки удовлетворяет цели, поставленные в Задании на доставку .

Б этой модели имеIОТСЯ. Конечно, нужны еще подходящие механизмы для поддержания Итак, все понятия, необходимые для удовлетворения вышеописанных требований, непрерывности существования объектов, поиска нужных объектов и т.п. Эти вопросы программной реализации в модели не рассматриваются, а вот в архитектуре программы они должны быть решены .

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

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

Из ол яция пр едметно й о бласти: до бавление u прикладных оп ерации Чтобы не смешивать обязанности объектов предметной области с функциями других частей системы, применим МНОГОУРОВНЕВУЮ АРХИТЕКТУРУ и выделим уровень пред­ метной области .

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

Naршрутный запрос (Tracking Query), имеющий доступ к прошлым и нынеш­ 1 .

ним манипуляциям с конкретным Грузом .

Служба резервирования (Booking Application) 2, позволяющая заказать но­ 2 .

вый Груз и при готовить систему к его обработке .

ОНО I-Ie следует путать употребляемое здесь слово служба с термином sernice - в данном контексте используется в общем, а не программнам смысле, хотя исполняемые функции аналогичны .

В оригинале слово application тоже создает путаницу с термином "приложение, прикладная про­ рuм еч. перев .

Грамм а". - П

ГЛАВА 7. РАБОТА С ЯЗЫКОМ: РАСШИРЕННЫЙ ПРИМЕР

Служба регистрации соБы'z'й (Inc ident Logging Appl icat ion), фикси­ 3 .

рующая любые манипуляции с Грузом (т.е. выводящая информацию, найденную маршрутным запросом) .

Эти классы операционного уровня являются всего лишь координаторами. Они не должны сами генерировать ответы на задаваемые вопросы - это работа уровня предмет­ ной области .

О тделени е сущн о стей от значе ний Рассмотрим по очереди каждый объект и выясним, кто из них представляет собой ин­ дивидуальные единицы, требующие отслеживания идентичности, а кто - просто набор значений. Вначале пройдем по четко определенным классам, а потом обратимся к менее очевидным случаям .

Клиент (Customer) Н ачнем с самого простого. Объект Клиент представляет лицо физическое (человека) или Iоридическое (фирму), т.е. самую что ни на есть индивидуальную сущность. У такого объекта, очевидно, есть идентичность, важная для пользователя, поэтому в модели он представляет собой сущность. Как отслеживать эту сущность? В некоторых случаях подойдет идентификационный налоговый номер, но для иностранной компании этот области. Мы обращаемся к сотруднику фирмы по доставке и узнаем, что у фирмы уже способ не годится. Этот вопрос требует консультации со специалистом по предметной есть база данных клиентов, в которой каждому Клиенту при первом же деловом контак­ те присваивается индивидуальный кодовый номер. Этот код уже используется для рабо­ ты в фирме, поэтому принятие его на вооружение в нашей про грамме позволит сохра­ нить совместимость систем. Вначале код будет вводиться вручную .

Г уз (Cargo) р Два одинаковых контейнера должны различаться, так что объекты Груз являются СУЩНОСТЯМ}!I. На практике все компании, занимающиеся доставкой грузов, присваива­ ют контрольные идентификационные номера каждому месту груза. Такие номера гене­ рируются автоматически, видны пользователю и в данном случае, скорее всего, сообща­ ются клиенту в момент резервирования заказа .

Ман ипуляция (Handling Event) и Переезд (Carrier Movement) Нас интересует каждое такое событие, поскольку нам необходимо знать, что происхо­ дит с грузами. Это отражение событий реального мира, которые обычно не взаимозаме­ няемы, так что объекты будут сущностями. Каждый Переезд будет идентифициро­ ваться по коду, который берется из расписания перевозок .

В дальнеЙllIИХ консультациях со специалистом выясняется, что Манипуляции можно од­ нозначно идентифицировать по совокупности номера Груза, времени выполнения и типа операции. Например, один и тот же Груз нельзя одновременно погружать и разгружать .

Местопол ожение (Location) Два географических пункта с одинаковыми названиями не обязаны быть одним и тем же. Уникальный ключ можно было бы построить из широты и долготы. Но это было бы не очень удобно, поскольку для большинства операций в этой предметной области гео­ графические измерения не представляют никакого интереса, да и сложноваты техничеЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМ ЕНТЫ ПРЕДМЕТНО- ОРИЕНТИРО В АННОГО.. .

ски.Более вероятно, что объект Местоположение будет частью некоей специальной гео­ графической модели, в которuй те или иные географические пункты будут различаться по маРПIрутам перевозок или еще каким-нибудь специфическим для предметной области способом. Поэтому достаточно будет произвольного, внутреннего, автоматически гене­ рируемого идентификатора .

История дост а вки (Delivery History) Тут дело обстоит сложнее. Истории доставки не взаимозаменяемы, т.е. являются СУIЦI IОСТЯМИ. Но ЛIобая История доставки однозначно ассоциирована с конкретным Грузом, и поэтому фактически не имеет собственной значимой идентичности. Ее инди­ нес, когда мы приступ им к моделированию агрегатов .

видуальность заимствована у Груза, который является ее владельцем. Все это станет яс­ Задание на дост а вку (Delivery Specijication) Хотя Задание на доставку представляет цель перевозки Груза, эта абстракция на из Историй самом деле не зависит от Груза, а выражает некоторое гипuтетическое состояние одной доставки. Мы надеемся, что История доставки, ассоциированная с Грузом, со временем станет соответствовать ЗадаНИIO на доставку, ассоциирован­ ному с тем же Грузом. Если два Груза направляются в одно и то же место, они могут со­ вместно использовать одно и то же Задание на доставку, но не могут этого делать с Историями доставки, пусть да)ке вначале они выглядят одинаковu (uбе пустые) .

Итак, Задания на доставку представляют собой ОБЪЕКТЫ-ЗНАЧЕНИЯ .

Р оль и д руги е а три буты Роль несет опрсделеННУIО информацию о той ассоциации, которой она соответствует, но у нее нет ни истории, ни непрерывности существования. ЭТО ОБЪЕКТ-ЗI-IАЧЕНИЕ, и его могут совместно использовать разные пары Груз-Клиент .

Другие атрибуты, такие как метки времени или имена, тоже представляют собой ОБЪЕКТЫ-ЗНАЧЕНИЯ .

Про ектирование асс оциаций в модели Ни одна из ассоциаций на исходной схеме не имеет приоритетного направления, а ведь двунаправленные ассоциации трудно переводить в программные конструкции .

Кроме того, выделение такого направления обычно выражает новое знание о предмете, что углубляет и саму мuдель .

Можно дать Клиенту прямую ссылку на каждый Груз, который он отправляет. Но дЛЯ IIОСТОЯННОГО клиента, регулярно делающего новые заказы, это будет обременительно. Кро­ ме того, понятие Клиента не привязано к конкретному Грузу. В большой системе Клиен-:I' может играть сразу много ролей по отношению ко многим объектам. Поэтому лучше не об­ ременять его такими специфическими обязанностями. Если нам нужна функция поиска Грузов по Клиентам, это можно сделать по запросу к базе данных. К этому вопросу мы еще вернемся позже в этой главе - в разделе, посвященном ХРАНИЛИЩАМ .

Если бы наша програмl\tl а вела учет грузовых судов, направление ассоциации от Пе ­ реезда к Манипуляции имело бы важное значение. I-Io в нашем деле имеет значение только учет Грузов. И направление ассоциации тuлько от Манипуляции К Переезду отражает праВИJIьное понимание предметной области. Также при этом упрощается проЛАВА 7. РАБ О ТА С Я ЗЫК ОМ : РАСШИРЕI-I I-IЫЙ ПРИМЕР 1 57 граммная реализация, сводясь к простой ссылке на объект, поскольку направление к множеству объектов отключено .

Обоснование остальных решений приведено на рис. 7.2 .

В нашей модели имеется одна циклическая ссылка: Груз знает свою ИсторИlO дос­ тавки, которая содержит последовательность манипулЯЦИЙ, которые в свою очередь ука­ метных областях, и в архитектуре программ они тоже бывают необходимы, но работать с зывают на Груз. Циклические ссылки возникают естественным образом во многих пред­ ними не так-то просто. Можно попробовать, используя чисто технические средства, избе­ жать необходимости хранить одну и ту же информацию в двух местах, между КОТОРЫМИ требуется синхронизация. В нашем случае можно реализовать простой, хотя и ненадежный вариант (Ha ]ava) в исходном прототипе: включить в ИсторИlO доставки объект Список (List), содержащий Манипуляции. Но в какой-то момент нам, вероятно, придется отка­ заться от этой коллекции в пользу поиска по базе данных с Грузом в качестве ключа. Этот вопрос еще всплывет и при выборе ХРАНИЛИЩ. Если запрос на просмотр истории делается сравнительно нечасто, такой подход должен дать хорошее быстродействие, упростить дора­ ботку и снизить затратность добавления новых МанипулЯЦИЙ. Если же такй запрос встре­ чается очень часто, то лучше иметь прямой указатель. Здесь идет речь о балансе между про­ стотой реализации и быстродействием. Модель же остается одной и той же; в ней есть ЦИК­ лическая ссылка и двунаправленность ассоциации .

Границы агрегатов Объекты Клиент, Местоположение и Переезд имеют собственную идентичность и совместно используются многими Грузами, поэтому они должны стать корневыми в своих собственных АГРЕГАТАХ, содержащих как их атрибуты, так и другие объекты, ко­ торые мы пока не будем рассматривать в подробностях. Груз также представляет собой очевидный корневой объект АГРЕГАТА, хотя не сразу понятно, где же провести четкую границу для него .

АГРЕГАТ Груз имеет право объединять в себе все, что не существовало бы, не будь в природе этого Груза т.е. Историю доставки, Задание на доставку, а также ма­ нипуляции. С Историей доставки все ясно. Никто не будет разыскивать ее в базе, не интересуясь при этом самим Грузом. Итак, прямой глобальный доступ к ней не нужен, а ее индивидуальность непосредственно происходит от Груза, поэтому История дос­ тавки }1деально укладывается в границы Груза как АГРЕГАТА, и при этом не обязана быть корневым объектом. Задание на доставку является ОБЪЕКТОМ-ЗНАЧЕНИЕМ, и от включения его в тот же АГРЕГАТ Груз никаких осложнений возникнуть не должно .

А вот Манипуляция дело другое. Раньше мы уже рассмотрели два возможных за­ проса к базе данных, которые могли бы разыскивать такие данные. Один, в котором ма­ нипуляции разыскиваются по базе для определенной Истории доставки вместо того, чтобы извлекаться из коллекции, имел бы локальный характер в АГРЕГАТЕ Груз. Другой использовался бы для нахождения всех погрузочных и подготовительных операций с грузом, соответствующих конкретному Переезду. Во втором случае возникает такое ощущение, что действия по манипулированию Грузом имеют самостоятельную объект­ ную ценность даже отдельно от самого Груза. Поэтому Манипуляция должна быть кор­ невым объектом своего собственного АГРЕГАТА .

–  –  –

\1/,

–  –  –

*

–  –  –

\1/ "

–  –  –

Рис. 7.2. Ограничение допустимых направлений в некоторых ассоциациях Выбор хранилищ в архитектурном проекте нашей программы имеется пять СУЩНОСТЕЙ, являющихся корневыми объектами АГРЕГАТОВ. Поэтому достаточно ограничиться только этими объек­ тами - остальным все равно нельзя иметь свои ХРАНИЛИЩА .

Чтобы решить, кто из этих кандидатов имеет право на хранилище, необходимо вер­ нуться к списку функциональных требований к программе. Чтобы сделать заказ через Службу резервир ования (Booking App l i cation), пользователь программы должен выбрать одного или нескольких Клиентов, которые будут играть различные роли (отправитель, получатель и т.д.). Итак, нам нужно Хранилище клиентов (Cus tomer Repos itory). Необходимо также будет задать Местоположение пункта, куда нужно отправить Груз, для чего следует создать Хранилище местоположеНИЙ (Location Repos itory) .

Служба регистрацИи событий ( Inc ident Logging Appl ication) должна дать пользователю возможность найти Переезд, на котором сейчас находится Груз, по­ этому необходимо и Хранилище переездов (Carri er Movement Repo s i tory) .

зитея, так что потребуется и Хранилище грузов (Cargo Repos i tory) .

Пользователь должен также иметь возможность сообщить системе, какой Груз перево­

–  –  –

Хранилища манипулЯЦИЙ (Иаndl ing Event Repos i tory) пока не будет, потому что в первом приближении мы решили реализовать ассuциацию с Историей доставки в виде коллекции, а в требованиях к программе ничего не сказано о том, дuлжна ли она уметь выяснять, что перевозится в том или ином Переезде. I-Io оба обстоятельства мо­ гут измениться, и тогда может понадобиться ХРАНИЛИЩЕ .

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

Приме р ра бочей функции : изменение места назначения груз а Нам звонит Клиент и говорит: "Ой, мы попросили доставить наш груз в Хакенсак, но на самом деле он нам нужен в Хобокене". Мы выполняем все пожелания наших клиен­ тов, так что система должна обеспечить подобное изменение маршрута .

сить и з авести новый, а потом сделать нужную замену в объекте Груз ( Cargo) с по­ Задание на доставку - это ОБЪЕКТ-ЗНАЧЕНИЕ, поэтому проще будет его сбро­ мощью sеt- метода .

–  –  –

Рис. 7.4. ХРАНИЛИЩА предоставляют доступ к корневым обьекmам выбранных АГРЕГАТОВ Пример рабочей функции : повторение заказ ов Пользователи говорят, что заказы, поступающие от одних и тех же клиентов, имеют тенденцию практически повторяться, поэтому имеет смысл использовать данные старых в ХРАНИЛИЩЕ и выбрать команду создания нового Груза на основе выбранного. Приме­ Грузов как прототипы для новых. В программе должна быть возможность найти Груз ним для этого архитектурный шаблон PROTOTYPE (ПРОТОТИП) [ 1 4] .

э'{о СУЩНОСТЬ и корневой объект АГРЕГАТА. Поэтому копировать его надо Груз тщательно; необходимо проанализировать, что должно случиться с каждым объектом или атрибутом, заключенным в границы этого АГРЕГАТА. Итак, по порядку .

История доставки. Нужно создать новый пустой объект, поскольку история ста­ • рого объекта здесь неприменима. Обычно так и бывает с СУЩНОСТЯМИ внутри границ АГРЕГАТОВ .

Роли клиентов. Необходимо скопировать Карту (Мар) или другую используемую • коллекцию, которая содержит ссылки на Клиентов по ключам - причем вместе с самими ключами, поскольку клиенr:I'ы' весьма вероятно, будут играть те же роли

ГЛАВА 7. РАБОТА С ЯЗЫКОМ: РАС Ш ИРЕННЫЙ ПРИМЕР

в новом заказе. Однако нужно проявить осторожность И не копировать сами объ­ екты. В конце концов у нас должны остаться ссылки на те же объекты-клиен'1'ы' на которые ссылался старый объект Груз, потому что это СУЩНОСТИ за предела­ ми АГРЕГАТА .

Контрольный номер. Новому заказу нужно присвоить новый контрольный иден­ • тификационный номер из того же источника, из которого мы бы получали его при создании целиком нового Груза .

Следует обратить внимание, что мы скопировали все внутри границ АГРЕГАТА Груз и внесли кое-какие изменения в копию, но не затронули никаких данных за пределами этого АГРЕГАТА .

Со здание о бъ е ктов Фа брики и к он структоры для объ екта Груз Даже если у нас есть сложно устроенная ФАБРИКА для объекта Груз (Cargo) или ес­ ли в качестве ФАБРИКИ используется другой Груз, как это было в сценарии повторения заказов, нам все равно нужен хотя бы простейший конструктор. От конструктора требу­ ется производить на свет объект, соблюдающий инварианты, или хотя бы, в случае СУЩНОСТИ, поддерживающий собственную уникальную идентичность .

На основании этих проектных р ешений можно создать следующий метод-ФАБРИКУ в объекте Груз .

pub l ic Cargo copyPrototype ( S t r i ng newTrackingI D )

–  –  –

Автономная ФАБРИКА могла бы также инкапсулировать процесс получения нового, автоматически сгенерированного идентификатора для нового Груза; в этом случае ей понадобился бы всего один аргумент .

pub l i c Cargo newCargo ( Cargo prototyp e ) Из этих ФАБРИК будет возвращаться один и тот же результат: объект Груз с пустой Историей доставки (Del ivery H i s tory) и нулевым Заданием на доставку (Del ivery Spec i f i cation) .

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

–  –  –

} ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРО В АННОГО.. .

В результате получаем новый Груз с новой ИС'l'орией ДОС'l'авки, указывающей на все тот же Груз. Конструктор Исории ДОС'l'авки используется исключительно корне­ вым объектом ее АГРЕГАТА, т.е. Груза, чтобы создание Груза было полностью инкапсу­ лировано .

До бавление объекта Мани пуляция Всякий раз, когда груз подвергается манипуляциям в реальном мире, кто-то из поль­ зователей вводит новый объект МаНИПУЛЯЦИЯ (Handl ing Event), используя Службу регисрации соБЫ'l'ий ( Inс idеnt Logging Appl ication) .

В каждом классе должны быть свои простейшие конструкторы. Поскольку Манипу­ ляция представляет собой СУЩНОСТЬ, в конструктор должны передаваться все атрибу­ ты, определяющие ее индивидуальность. Как уже говорилось, МаНИПУЛЯЦИЯ однозначно определяется по сочетанию идентификатора Груза, времени выполнения и типа собы ­ тия.

Кроме этого, в МаНИПУЛЯЦИИ имеется еще только один атрибут - ассоциация с Пе ­ создающий корректный объект Манипуля:ция: (Handl ing Even t):

реездои (причем даже не у всех типов МаНИПУЛЯЦИЙ). Вот простейший конструктор,

–  –  –

Как правило, атрибуты СУЩНОСТИ, не обязательные для ее идентификации, можно до­ бавить позже, а не при создании. В нашем случае все атрибуты МаНИПУЛЯЦИИ будут опре­ делены в первой транзакции, после чего больше изменяться не будут (кроме разве что ис­ правления ошибок ввода даты). Поэтому будет удобно и более выразительно с точки зре­ ния клиентского кода добавить в объект Манипуляция простой МЕТОД-ФАБРИКУ дЛЯ каждого типа события, который бы принимал все нужные аргументы. Например, "событие погрузки" не может обойтись без объекта Переезд (Carrier Movemen t) .

–  –  –

много раЗЛИЧНЬJХ специализированных классов МаНИПУЛЯЦИЙ, от погрузки И разгрузки ми. Можно создать для этой цели много подклассов, можно применить сложную ини­ до опечатывания, помещения на хранение и других операций, не связанных с Переезда ­ циализацию, а можно сочетать то и другое. Добавляя МЕТОДЫ-ФАБРИКИ в базовый масс МаНИПУЛЯЦИЯ для каждого типа манипуляций с грузами, мы абстрагируем создание эк­ земпляров и освобождаем клиентский код от необходимости знать внутреннее устройст­ во класса. Пусть ФАБРИКА отвечает за то, чтобы правильно распознать, экземпляр какого класса нужно создать и как именно его инициализировать .

Но, к сожалению, не все так просто. Создание экземпляров осложняется цикличе­ скими ссылками: из Груза на Исорию досавки, затем на Собие исории ГЛАВА 7. РАБОТА С ЯЗЫКОМ: РАСШИРЕННЫЙ IIРИМЕР 1 63 (Hi s tory Event) и в конце концов снова на Груз. Истории доставки содержит кол­ лекцию Манипул5ЩИЙ, относящихея к соответствующему Грузу, и новый объект можно добавлять в эту коллекцию только в рамках транзакции. Если этот обратный указатель не создать, связи между объектами будут разорваны .

–  –  –

,

–  –  –

,,, "

–  –  –

Создание обратного указателя можно инкапсулировать в ФАБРИКЕ (и сохранить на уровне предметной области, где ему и место). Но сейчас мы рассмотрим альтернативный подход, который позволит совсем убрать эту "неуклюжую" взаимосвязь .

Перерыв на рефакторинг:

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

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

Необходимость обновлять Историю доставки при добавлении Манипуляции во­ влекает в транзакцию АГРЕГАТ Груз. Если другой пользователь в это же самое время вносит в Груз изменения, то транзакция Манипулsщии срывается или задерживается .

Ввод Манипулsщии это операционная деятельность, которая должна выполняться быЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРО В АННОГО.. .

стро просто, поэтому следует выдвинуть важное требование - вводить Манипуляции И без КО1-lкуре1-lт1-l0сти, конфликтов данных. Это требует от нас пересмотреть архитектуру программы .

Если заменить коллекцию МанипулЯЦИЙ в Истории доставки на запрос к базе данных, это позволит нам добавлять новые Манипуляции, не создавая проблем с тран­ закционной целостностью за пределами АГРЕГАТА. Такое изменение в программе позво­ лит заве ршать подобные транзакции без вмешательства. Если вводится много Манипу­ лЯЦИЙ, а запросов выполняется сравнительно немного, этот подход становится более эффективным. На самом деле же, если на техническом уровне для хранения данных ис­ пользуется реляционная база, то, скорее всего, для эмуляции коллекции все равно ис­ пользуется запрос. Замена коллекции запросом позволяет также облегчить согласование циклических ссылок между Грузом и Манипуляцией .

Чтобы реализовать механизм запросов, добавим в программу ХРАНИЛИЩЕ дЛЯ МАНИПУЛЯЦИЙ. Хранилище манипулЯЦИЙ (Handl ing Event Repos i tory) обеспе­ чит выполнение запросов по Манипуляциям, относящимся К определенному Грузу .

Кроме того, оно может обслуживать зап р осы, оптимизированные на поиск определенной специализированной информации. Например, если часто выполняются обращения к Ис ­ тории доставки для просмотра последней погрузки или выгрузки, чтобы по ним опре­ делить текущее состояние Груза, то можно скомпоновать такой запрос, который будет возвращать только нужную Манипуляцию. А если понадобится, например, запросить все ГрУЗЫ, погруженные на транспорт для конкретного Переезда, такой зап р ос тоже можно будет легко добавить .

Из-за всего этого у Истории доставки теперь нет постоянно хранимого состоя­ на этом этапе нет необходимости держать его под рукой. Историю доставки лег­ нияко воспроизвести всякий раз, когда она нужна для ответа на определенный вопрос. И хо­ тя СУЩНОСТ Ь в этом случае переживает регулярное восстановление, непрерывность ее и тем же Грузом .

СУlдествования между различными воплощениями обеспечивается ассоциацией с одним Теперь организовать и поддерживать циклическую ссылку ничего не стоит. Фабрика ГРУЗОВ (Cargo Fac tory) упрощается, поскольку больше не обязана добавлять пустую Историю доставки к новым экземплярам объектов. Можно немного сэкономить на объеме базы данных, причем реальное количество постоянно хранимых объектов может уменьшиться значительно, а это ограниченный ресурс в некоторых объектных базах дан­ ных. Если обычная практика такова, что пользователь редко запрашивает статус Груза до его прибытия к месту назначения, то при этом подходе удается избежать большого объема ненужной работы .

С другой стороны, если используется объектная база данных, то прослеживание ас­ социации или коллекции выполняется, скорее всего, намного быстрее, чем запрос к ХРАНИЛИЩУ. Если в ходе работы часто запрашивается полная история манипуляций с грузом, а не время от времени - его последнее местоположение, то соображения бы­ стродействия могут оказаться в пользу коллекции. Кроме того, не забудем, что добав­ де?") пока никто не просил, и может вовсе не попросить, так что не нужно слишком ленную в программу гипотетическую возможность ( "Что перевозится в этом Переез ­ тратиться на ее реализацию .

и д аже в этой упрощенной системе можно найти множество примеров. Но важно по­ Подобные альтернативные варианты и компромиссы встречаются повсеместно, нимать, что все это степени свободы маневра внутри одной и той же модели. Пра­ вильно выполнив моделирование ОБЪЕКТО В -ЗНАЧЕНИЙ, СУЩНОСТЕЙ и их АГРЕГАТО В,

ГЛАВА 7. РАБОТА С ЯЗЫКОМ: РАСШИРЕННЫЙ ПРИМЕР

мы снизили побочные эффекты от изменений архитектуры. Например, в нашем случае все изменения инкапсулированы в границах АГРЕГАТА Груз. Правда, потребовалось еще введение хранилища манипуляЦИЙ, но при этом не пришлось ничего переделы­ вать в самих манипуляциях (правда, в зависимости от особенностей технической реа­ лизации ХРАНИЛИЩ кое-какая переделка все-таки может понадобиться) .

–  –  –

* " *

–  –  –

Hi s tory) в виде запроса К базе данных облегчает добавление новых Манипуляций и ликвuдuрует КОН­ Рис. 7.6. Реализация КОJUlе1Щии ИaJumуЛJЩИЙ (Handling Even ts) в ИСТОРИИ доставю.r (Delivery флиюnы nараллелъно ввoдuмыx данных в АГРЕГАТЕ lpуэ (Cargo) М одули в м одели ГРУЗ0 пост ав ок Пока что в нашей работе участвовало так мало объектов, что вопрос о разбиении на модули даже не поднимался. Теперь давайте рассмотрим несколько более "громоздкую" часть модели поставок (хотя, конечно, по-прежнему упрощенную) и оценим, как ее орга­ низация в виде МОДУЛЕЙ повлияет на модель .

На рис. 7.7 показано аккуратное разбиение модели, выполненное гипотетическим ЭН­ ния под влиянием технической инфраструктуры, поставленной в главе 5. В данном слу­ тузиастом идей нашей книги. Эта схема напоминает нам о проблеме модульного члене­ чае объекты группируются согласно архитектурным шаблонам, по которым они по­ строены. В результате сваливаются в одну кучу объекты, концептуально имеющие между 1 66 ЧАСТЬ 1 1. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ П РЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

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

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

Вместо того чтобы заниматься подобным, нам следует искать связанные понятия и сосредоточиться на цели проекта. Как и в случае менее масштабных проектных решений, существует много способов сделать это. На рис. 7.8 показан самый "прямолинейный" .

Имена модулей на рис. 7.8 пополняют язык разработчиков. Наша компания выполня­ ет доставку по адресам клиентов, поэтому мы имеем право взимать с них orиzaтy. Торго­ во-обслуживающий персонал напрямую имеет дело с клиентами, заключая с ними дого­ воры. Технический персонал непосредственно выполняет операции доставки, привозя груз К месту назначения. Бухгалтерия ведает вопросами orиzaты, выставляя счета­ фактуры согласно оговоренным в договоре клиента ценам. Такова информация, которую несет данный набор модулей .

Конечно, это интуитивное разбиение можно дальше улучшать методом последова­ тельных приближений, а то и вовсе заменить на другое, но в нынешнем виде оно уже по­ могает в ПРОЕКТИРОВАНИИ ПО МОДЕЛИ и вносит вклад в развитие ЕДИНОГО ЯЗЫКА .

Новая функция : ра спределение заказ ов До этого момента мы занимались "подгонкой" модели под первоначальные требова­ ния. Теперь нам предстоит добавить некоторые новые и важные функции .

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

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

Нужная для этого информация располагается в двух местах, которые необходимо опрашивать, используя Службу резервирования (Booking App l i c a t i on), ко­ торая и будет решать, принять заказ или нет. Примерная схема движения информа­ ции будет выглядеть так .

–  –  –

Связь между двумя системами мы сейчас р аботаем. Если Служба резерв ировании будет взаимодействовать с ней на­ Система управления поставками не создана на основе этой модели, с которой п рямую, то нам придется приспосабливаться к архитектуре и технологиям той другой системы. Из-за этого станет труднее П РОЕКТИРО ВАТ Ь П О МОДЕЛИ и придерживаться

ГЛАВА 7. РАБОТА С ЯЗЫКОМ: РАСШИРЕННЫЙ ПРИМЕР

ЕДИНОГО ЯЗЫКА. Чтобы этого не делать, давайте разработаем новый класс, в обязанности которого будет входить трансляция между нашей моделью и языком систеиы управ ­ лении поставками. Мы не будем создавать трансляторный механизм самого общего назначения. Новый класс будет открывать для обозрения только те возможности, кото­ рые нам нужны, и абстрагировать их заново в терминах нашей модели. Таким образом, этот класс будет выступать в качестве П РЕДОХРАНИТЕЛ Ь НОГО УРОВНЯ (ANTICORRUPTION LAYER), который рассматривается позже в главе 14 .

Поскольку этот класс - интерфейс к Системе управления поставками, на пер­ вый взгляд может показаться, что ему нужно дать имя Интерфейс управления по­ ставками (Sales Management Interface) или что-то в этом роде. Но тогда мы упустим возможность переформулировать проблему на наш ем языке в виде, более удоб­ ном для нас. Давайте определим СЛУЖБУ для каждой из функций распределения зака­ зов, которые нам потребуются от другой системы. А реализуем мы эти службы с помо­ щыо класса, имя которого будет отражать его обязанности в нашей системе: Контролер распределеНИИ (Аl lосаtiоn Checker) .

Если понадобится еще какая-нибудь интеграция помимо этой (например, использо­ вание базы данн ы х клиентов из систeиы управпения поставками вместо нашего собственного ХРАНИЛИЩА Клиентов), то можно создать еще один транслятор, СЛУЖБЫ которого будут в ы полнять соответствующие обязанности. Может оказаться полезным и наличие класса более низкого уровня наподобие Интерфейса систеиы управления поставками (Sales Management Sys tem Interface), который бы управлял меха­ низмами коммуникации с другой системой, но не отвечал бы за трансляцию смысловых понятиЙ. К тому же он был бы спрятан за Контролером распределения, следователь­ но, не виден в модели предметной области .

Усове рш енств ование модели: введение подразделений

Итак, мы наметили контуры взаимодействия двух систем. Теперь поставим задачу:

какой интерфейс нужно иметь, чтобы ответить на вопрос "Сколько можно заказать Гру­ за данной разновидности?". Проблема состоит в том, чтобы определить, что такое "разновидность" Груза, поскольку в модели предметной области никакой классифика­ ции Грузов еще нет. В Системе управлении поставками классификация Грузов на категории выполняется по списку ключевых слов, и наши типы должны соответствовать этому списку. В качестве аргумента можно было бы передать коллекцию строк, но при этом упускается другая возможность: заново абстрагировать предметную область другой системы. Необходимо обогатить нашу модель знанием о том, что существуют разные ка­ тегории грузов. И проработать новое понятие модели нужно совместно со специалистом в предметной области .

Иногда (как будет говориться в главе 1 1 ) идею проектного решения в модели может подсказать аналитический шаблон (analysis pattem). В [ 1 1 ] описывается шаблон, помо­ гающий реш ить эту проблему: УЧАСТОК РАБОТ (ENTERPRISE SEGMENT). УЧАСТОК РАБОТ представляет собой набор мер и критериев для логического выделения той или иной час­ ти из прикладной деятельности, моделируемой в программе. Среди этих мер-критериев могут быть такие, которые мы уже упоминали в связи с деятельностью по доставке гру­ зов, а также меры времени, - например, месяцы или дни. Использование этого понятия в щает интерфейс. Класс с именем Участок работ (Enterpri s e Segment) появится в нашей модели распределения заказов придает модели больше выразительности и упро­ нашей модели и программной архитектуре как дополнительный ОБЪЕКТ-ЗНАЧЕНИЕ, ко­ торый нужно будет создать для каждого Груза .

–  –  –

Контролер распределения обеспечивает трансляцию между Участками работ и именами категорий во внешней системе. Соответственно, Хранилище грузов должно также обслуживать запросы по Участкам работ. В обоих случаях объект Участок рабо'Z' помогает выполнять эти операции, не нарушая его инкапсуляции и не усложняя их собственной реализации. (Следует помнить, что Хранилище грузов выдает в ответ на запрос результат подсчета, а не коллекцию экземпляров.) В этой архитектуре все еще есть несколько недостатков .

Мы передали Службе резервирования работу по применению следующего пра­ 1 .

вила (делового регламента): "Заказ на доставку Груза принимается, если выде­ ленное для его Участка работ пространство больше, чем уже заказанный объем плюс объем нового Груза". Но следить за исполнением делового регламента - это обязанность уровня предметной области, которую не следует выносить на опера­ ционный уровень .

Неясно, как именно Служба резерв ирования порождает Учас'Z'ок работ .

2 .

Обе эти обязанности кажутся естественными для Контролера распределения .

Если изменить его интерфейс, эти две СЛУЖБЫ можно разделить и тем самым добиться более четкого взаимодействия .

в том, что Система Единственное серьезное ограничение, накладываемое такой интеграцией, состоит управления поставками (Sales Management Sys tem) не должна использовать такие критерии-меры, которые Контролер распределения не может преобразовать в Участки работ. (Без применения шаблона УЧАСТОК РАБОТ аналогич­ ное ограничение вынуждало бы систему управления поставками пользоваться только ГЛА В А 7. РАБОТА С ЯЗЫКОМ: РАСШИРЕННЫЙ ПРИМЕР 171 теми критериями, которые допустимы в запросах к Хранилищу грузов. Этот подход имеет право на существование, но тогда функции системы управления поставками "просачиваются" в другие части предметной области. В нашей же архитектуре Хранили­ в системе управления поставками доходит только до Контролера распределения, ще грузов должно уметь работать только с Участками работ, а эффект изменений к торый с самого начала и задумывался как ФАСАДНЫЙ ОБЪЕКТ.)

–  –  –

Рис. 7. 1 1. Передача обязанностей уровня предметной области от Службы резервирова ­ ния (Booking Appl i ca tion) Конолеру распределения (Al loca t i on Checker)

–  –  –

ственно связанная с остальными объектами архитектуры предметной области. Но вот его внутренняя реализация предоставляет определенные возможности для решения проблем быстродействия, если таковые возникнут. Например, если Система управления по ­ ставками работает на другом сервере (физически находящемся в другом месте), то за­ траты на пересылку данных могут оказаться существенными, ведь на каждую проверку Контролера выполняются два обмена сообщениями. Альтернативы второму сообще­ нию, которое требует от систеиы управления поставками ответить на вопрос, мож­ но или нет принять заказ на определенный груз, нет. Но первое сообщение, которое соз­ дает Участок работ для данного груза, основано на относительно статических данных и операциях, сравнимых с собственно операциями распределения. Одно из возможных проектных решений состоит в том, чтобы кэшировать эти данные на сервере, где распо­ ложен Контролер распределения, и тем самым уменьшить объем пересылки сообrце­ ний вдвое. Но эта оптимизация не проходит даром: структура программы усложняется, и теперь нужно каким-то образом синхронизировать дублирующиеся данные. Но если для распределенной системы именно быстродействие является критическим парамет­ ром, гибкость размещения данных может оказаться удачным проектным решением .

Вот и все. Интеграция приложений могла бы превратить нашу простую, концепту­ Ито ги ально связную архитектуру в хаотическую кашу. Но с помощью предохранительного уровня, служб и разделения на участки работ нам удалось интегрировать функции Сис ЧАСТЬ 11. СТРУКТУРНЫЕ ЭЛЕМЕНТЫ ПРЕДМЕТНО-ОРИЕНТИРОВАННОГО.. .

управления поставками в нашу систему резервирования заказов без наруше­ темы ния стиля и структуры, а также с развитием предметной области .

обязанности последний вопрос по проектированию архитектуры: почему бы не передать Грузу и по созданию Участка работ? На первый взгляд, это решение кажется правильным: если все нужные для создания данные находятся в объекте Груз, то можно лению, не все так просто. Участки работ определяются так, чтобы их границы прохо­ было бы сделать и сам объект Участок работ производным атрибутом Груза. К сожа­ дили удобно с точки зрения деловой стратегии предприятия. Одни и те же СУЩНОСТИ могут подразделяться на логические фрагменты по-разному в зависимости от целей дея­ тельности. Мы выделили свой участок работ для конкретного Груза с целью резервиро­ вания заказа, но, например, с точки зрения налогового учета он мог бы занимать совер­ шенно другой участок. Даже и те Участки работ, которые связаны с распределением заказов, могут со временем меняться, если Система управления поставками под­ зу пришлось бы иметь определенные знания о Контролере распределения, что дале­ вергнется реструктуризации, например, в связи с новой деловой стратегией. Тогда Гру ­ ко выходило бы за пределы его концептуальных обязанностей и перегружало бы его ме­ тодами для создания конкретных типов Участков работ. Поэтому обязанности по созданию таких значений естественным образом должны ложиться на тот объект, кото­ рый знает правила сегментирования деятельности, а не на тот, который содержит подчи­ няющиеся этим правилам данные. Правила можно было бы вынести в отдельный объект "Стратегия" ("S trategy"), который можно передать в Груз и тем самым позволить ему создать Участки работ. Это решение, похоже, выходит за пределы поставленных перед нами задач, но оно могло бы стать одним из вариантов будущих проектных реше­ ний, так как не должно иметь никаких пагубных последствий .

ГЛАВА 7. РАБОТА С ЯЗЫКОМ : РАСШИРЕННЫЙ ПРИМЕР 1 73 Углубляющий рефакторииг В части 11 этой книги была заложена основа' для поддержания точного соответствия ме­ жду моделью и ее программной реализацией .

Использование проверенного набора базо­ вых структурных элементов наряду с единым и последовательным языком привносит в работу программиста систематичность .

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

В процессе разработки полезных моделей необходимо понять три истины .

Создание сложных, хорошо проработанных моделей предметных областей воз­ 1 .

можно, и они стоят затраченного на них труда .

Практически не существует другого способа построить такую модель, кроме ите­ 2 .

рационного процесс а рефакторинга с тесным взаимодействием между специали­ стами предметной области и программистами, желающими узнать о ней больше .

Реализация и успешное использование таких моделей могут потребовать высокой 3 .

квалификации в области проектирования и моделирования .

Ур овни р е фактор инга Рефакторинг - это такая реструктуризация программы, в результате которой не из­ меняются ее функциональные возможности. Вместо того чтобы планировать масштаб­ ные проектные решения, разработчики последовательно вносят в код небольшие, дис­ кретные структурные изменения, после которых выполняемые программой функции не изменяются, а структура программы становится более понятной и гибкой. Можно срав­ нительно безопасно экспериментировать с кодом, если в наличии есть набор автомати­ зированных модульных тестов. Такая процедура избавляет разработчиков от необходи­ мости планировать далеко наперед .

Но практически вся литература по рефакторингу посвящена механическим измене­ ниям в коде, улучшающим удобство чтения или какие-нибудь мелкие детали. А вот ме­ тодика "рефакторинга по шаблонам" 1 позволила бы переориентировать этот процесс на задачи более высокого уровня, в которых разработчик ищет возможность применить к программе общепринятые архитектурные шаблоны. И все-таки даже это - в основном технический взгляд на качество программной архитектуры .

Чтобы рефакторинг радикально повлиял на надежность и жизнеспособность систе­ мы, он должен либо мотивироваться углубленным пониманием предметной области, ли­ бо улучшать качество представления модели в результате использования программного кода. Эта р азновидность рефакторинга, впрочем, не отменяет рефакторинг по архитек­ турным шаблонам или микрорефакторинг (и тот, и другой должны выполняться непре­ рывно). Наоборот, она накладывается на них как более высокий уровень рефакторинг 1 Архитектурные шаблоны как объект рефакторинга вкратце упоминались в [Gamma et al., 19951, Затем уже Джошуа Кериевский Qoshua Kerievsky) придал методике рефакторинга по шаблонам более зрелую и полезную форму [Kerievsky, 2003] .

ЧАСТЬ 1 1 1 по углубленной модели, или углубляющий рефакторинг. Рефакторинг на основе новых знаний о предметной области часто состоит из последовательности микрорефакторин­ гов, но побуждением к нему служит не просто желание улучшить код. Микрорефакто­ ринг в данном случае - способ разбить процесс движения к углубленной и осмысленной модели на технически удобные стадии. Цель состоит в том, чтобы разработчик понимал не только, что делает код, но и почему код делает то, что он делает, и чтобы это понима­ ние было вызвано постоянным общением со специалистами в предметной области .

в каталоге из книги по рефакторингу [ 1 2] перечислено большинство обычных прие­ мов микрорефакторинга. Поводом для применения каждого из них обычно становится какая-то проблема в самом коде. В противоположность им модели предметной области подвергаются настолько разнообразным трансформациям по мере приобретения новых знаний и более глубокого пони мания предмета, что полный их каталог составить попро­ сту невозможно .

Моделирование - деятельность неструктурированная и неформальная по своей сути, как всякое исследование. Углубляющий рефакторинг2 выполняется так, как это диктуют разработчику новые знания предметной области и новые, более глубокие выводы из этих знаний. Конечно, неплохо иметь под рукой опубликованное собрание удачных моделей (об этом говорится в главе 1 1 ), но оно может и увести в сторону, поскольку моделирование предметной области нельзя сводить к набору готовых рецептов или применению инстру­ ментов из стандартного набора. Моделирование и проектирование архитектуры программ3 требуют творческого подхода. В следующих шести главах будут прорабатываться некото­ рые специфические методики для усовершенствования моделей предметных областей, а также рассматриваться программные архитектуры, на основе которых они созданы .

Углубленные модели Традиционно методику объектного анализа объясняют так: надо найти в техническом задании на программу (т.е. документации, которая дает список требований к ней) ключе­ вые существительные и глаголы, после чего выбрать их в качестве объектов и методов первого приближения. Это объяснение объектного моделирования будет слишком уп­ рощенным, но для преподавания начинающим вполне сойдет. Однако, по правде говоря, модели первого приближения обычно страдают наивностью и построены на основе по­ верхностного знания о предмете .

и предложил в качестве объектов первоначальной модели корабли и контейнеры. В са­ Например, как-то раз я работал над программой для обслуживания грузоперевозок мом деле, ведь корабли перемещаются с места на место, а контейнеры то ассоциируются с ними, то теряют эту ассоциацию посредством операций погрузки и разгрузки. Да, фи­ зическая работа по перемещению грузов описана довольно точно, но вот с точки зрения программного обеспечения, нужного для ведения дела, такая модель бесполезна .

НИИ,

–  –  –

НИЯ, тогда как в книге речь идет о разработке надежной, работоспособной структуры. Поэтому с англ. design. Но в русском языке это слово обозначает создание красивого визуального оформле­ (ВОЗМОЖНО, к неудовольствию многих программистов) я избегаю слова "дизайн". Более точно го­ ВОРИТЬ "проектирование" или даже "конструирование", но только в отношении процесса, тогда как Прuм еч. перев .

"архитектура" обозначает его результат. УГЛУБЛЯЮЩИЙ РЕФАКТОРИНГ Постепенно, за много итераций, через несколько месяцев совместной работы со спе­ циалистами по перевозкам, мы создали совершенно другую модель. Неспециалисту она казалась гораздо менее очевидной, но зато специалисты были довольны. Акценты см е­ стились на деловые операции по доставке груза .

Корабли никуда не делись из модели, но теперь это были абстракции в виде "рейсов" - отдельных перевозок на корабле, поезде или другом транспортном средстве .

Сам по себе корабль как объект стал второстепенным; его могли заменить в последний момент из-за необходимости ремонта или по скользящему графику, тогда как рейс про­ ходил строго по плану. Контейнер же исчез из модели вообще. Точнее, он все-таки фигу­ рировал в программе управления грузами в другой, более сложной форме, но в контексте исходной программы он превратился в мелкую подробность выполнения деловой опера­ ции. Физическое перемещение груза отступило на второй план по сравнению с переда­ чей юридической ответственности за груз. А на первый план вышли менее очевидные объекты, такие как "транспортные накладные" .

Когда новые моделировщики появлялись в проекте, каковы были их первые предло­ жения? Да все те же: надо добавить недостающие классы "корабль" и "контейнер". Но эти люди не были глупы - они просто еще не приобрели знакомство с предметом .

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

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

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

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

Легкость в использовании и удобство внесения изменений связаны с определенными требуют тщательной проработки. "Гибкая архитектура" и способы ее построения как раз характерными особенностями программной архитектуры. Они не особенно сложны, но и составляют предмет главы 10 .

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

Кроме удобства внесения изменений в код, гибкая архитектура способствует еще и усовершенствованию самой модели. ПРОЕКТИРОБАНИЕ ПО МОДЕЛИ (MODEL-DRIVEN DESIGN) стоит на двух "столпах": пусть углубленная модель делает возможной вырази­ тельную архитектуру, но ведь и сама архитектура помогает в развитии предметного знаЧАСТЬ 111 ИИЯ, заключенного в модели, если достаточная ее гибкость позволяет разработчику экс­ периментировать, а достаточная наглядность - ясно видеть, что происходит. Эта вторая половина цикла обратной связи весьма важна, поскольку нужная нам модель - это не просто красивый набор идей, а основа работы программной системы .

Процесс по знания Чтобы построить программную архитектуру, хорошо приспособленную к решению прикладной задачи, вначале следует создать модель, которая бы выражала основные по­ нятия (концепции) предметной области. Активный поиск этих концепций и внедрение их в архитектуру составляют тему главы 9 .

Из-за тесной связи между моделью и архитектурой процесс моделирования затягива­ ется, когда становится трудно ВЫПОЛНЯТЬ рефакторинг над кодом. В главе 1 0 обсуждает­ ся вопрос, как правильно писать программы, которые другим разработчикам (да и себе самому тоже) было бы легко дорабатывать и расширять их функциональные возможно­ сти. Такая работа всегда сопровождается усовершенствованием модели, поэтому часто требует сложных методов проектирования архитектуры и более строгого определения основных понятий .

Для выстраивания нового знания и новых понятий В виде модели обычно приходится полагаться на творческие способности, а также метод проб и ошибок. Но иногда оказыва­ ется, что можно следовать шаблонному образцу, который кто-то до вас уже открыл и описал. В главах 1 1 и 1 2 рассматривается применение "аналитических шаблонов" шаблоны - это не готовые решения, но с ИХ помощью можно сузить область поиска (analysis pattems) и "архитектурных [проектных] шаблонов" (design pattems)4. Такие и более продуктивно осуществить переработку знания .

Но все же я начну часть 1 1 1 с самого волнующего события во всем предметно­ ориентированном проектировании. Бывает так, что как только выстроена хорошо спро­ ектированная по модели архитектура и подобран хороший набор четких понятий, проис­ ходит качественный скачок - открывается возможность превратить программу в нечто более выразительное и разностороннее, чем могло ожидаться ранее. Это может означать как добавление новых функциональных возможностей, так и замену большой порции не­ гибкого, маловыразительного кода на простое и гибкое выражение углубленной модели предметной области. Такие скачки, конечно, случаются не каждый день. Но они настоль­ ко ценны сами по себе, что упускать их нельзя - нужно суметь распознать потенциаль­ ную возможность такого скачка и в полной мере ее реализовать .

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

4 К сожалению, это выражение уже получило в русском языке неточный перевод-кальку "шаблоны проектирования". Между тем неплохо передает суть явления выражение "архитектурный образец". Здесь же используется противоположный смысл слов analysis (анализ) и design (проектирование, разработка) как двух взаимодополняющих видов деятельности: аналити­ ческой и синтетической. Прuмеч. перев .

У ГЛ УБЛЯЮЩИЙ РЕФАКТОРИНГ

ГЛАВА 8

–  –  –

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

Медленно, но уверенно разработчики ассимилируют знание и перерабатывают его в компактную форму модели. Углубленные модели могут возникать постепенно, после серии небольших операций рефакторинга, каждый раз выполняемых над одним объектом: здесь от­ корректировaтrи ассоциацию между тем и этим, там передали обязанности оттуда туда .

Но бывает, что постепенный рефакторинг открывает дорогу чему-то новому совсем не так систематично. Каждое усовершенствование кода и модели открывает разработчи­ ку глаза на что-то новое. А ясность восприятия создает потенциальную возможность для качественно нового вывода или идеи. И поток модификаций вдруг приводит к модели, которая соответствует реальности и приоритетам пользователя на более глубоком уров­ не, чем ранее. Универсaтrьность и наглядность внезапно возрастают, а сложность при этом куда-то испаряется .

ся в том, чтобы понять, что же происходит и что в связи с этим делать. Чтобы донести до Такого рода скачок -- это не стандартный прием. Это событие. Трудность заключает­ читателя, на что похоже это ощущение, я расскажу реальную ИСТОРИIО о проекте, над ко­ торым я работал несколько лет назад, - историю о том, как нам yдaтrocь построить очень ценную и глубокую модель .

И стория успеха в которой заключались некоторые основные знания о предметной области, а также к ар­ Просидев над рефакторингом долгую нью-йоркскую зиму, мы пришли к модели, хитектуре, которая более-менее давала возможность работать. А работали мы над цен­ тральной частью большой программы по управлеНИIО синдицированными кредитами в инвестиционном банке .

Если компания Intel хочет построить завод стоимостью миллиард долларов, ей для этого потребуется слишком большой кредит, который не сможет выдать в одиночку ни одна кредитная организация. Поэтому кредиторы формируют синдикат, объединяющий их ресурсы для совместной поддержки предприятия (см. примечание ниже). Инвести­ ционный банк обычно главенствует в таком синдикате, координируя финансовые тран­ закции и оказывая другие услуги. Наш проект заключался в разработке программного обеспечения для обслуживания этого процесса .

Что такое п редприятие "Предприятие" в данном контексте - это не заводские помещения с оборудованием .

В этом проекте, как и во многих других, мы обогатили словарь нашего ЕДИНОГО ЯЗЫКА специализированной терминологией из языка специалистов по предметной области .

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

–  –  –

Модель неплоха, но.. .

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

–  –  –

ставляет долевой вклад конкретного инвестора в общий Кредит (Loan), пропорцио­ нальный его доле в Предприятии (Fac i 1 i ty) .

Но были и тревожные признаки. Мы постоянно "спотыкались" о неожиданные требо­ вания, которые усложняли структуру программы. Вот один из главных примеров: посте­ пенно до нас дошло, что доли в Предприятии имеют только рекомендательный харак­ тер по участию в любых выборках средств по кредиту .

Когда заемщик запрашивает свои деньги, глава синдиката может востребовать соот­ ветствующие доли у любых его членов. После такого требования член синдиката обычно выкладывает нужную сумму, НО он может и вступить в переговоры с другим членом син­ диката о том, чтобы вложить меньшую (или большую) сумму. Мы приспособились к этому обстоятельству, введя в модель Кредитные поправки (Loan Adj us tments) .

–  –  –

–  –  –

Рис. 8.2. ПоследоватеЛЬ1lые поправки к модели для реше1lИЯ nроблем. КреДIl!1'ные попраSЖIl (Loan Adj us tmen ts) описывают вычеты из доли, которую кредитор ИЗ1lачалЬ1l0 соzлашался предоставить ПреДnPИЯ!1'1llD (Faci li ty) У сове р шенствования такого рода позволяли нам решать п р облем ы в текущем р ежи­ ме, по мере прояснения правил, по котор ы м вы полнялись различн ы е транзакции. Но сложность ар хитектуры возрастала, а бы стро вы йти на надежную, устойчивую функцио­ н ал ьн ость не получалось .

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

ГЛА В А 8. КАЧЕСТВЕННЫЙ СКАЧОК 1 83 Скачок Наконец на нас снизошло озарение и мы поняли, что же было не так .

Б нашей модели Предприятие (Fac i l i ty) и доли Кредита (Loan) были связаны таким образом, кото­ рый не сооmвеmсmвовOJl реOJlЬНОМУ положению дел. Это открытие имело широкий резо­ нанс. С помощью специалистов по банковскому делу, под их одобрительные кивки и чего греха таить - вопросы, почему мы так долго возились, мы набросали новую модель на доске. Хотя ее детали еще не выкристаллизировались, мы уже знали, какая новая осо­ бенность является критически важной для модели: доли в Кредите и доли в Предпри­ _тин могли изменяться независимо одна от другой. Сделав этот фундаментальный вы­ вод, мы приступили К прохождению многочисленных сценариев, используя наглядное представление новой модели, которое выглядело примерно так .

–  –  –

Рис. 8.3. Распределение выборки средств на основе долей в Предприятии (Faci l i ty) На рис. 8.3 показано, что заемщик решил получить начальную сумму в 50 млн. долларов из тех 1 00 млн., которые ему обещаны в рамках данного Предприятия. Трое кредиторов делят этот взнос в долях, пропорциональных их заявленным долям в Предприятии. В ито­ ге пятидесятимиллионный Кредит выдается кредиторами в показанной пропорции .

–  –  –

Далее, на рис. 8.4 заемщик получает еще 30 млн. долларов, доводя освоенный им Кредит до 80 млн.; это все еще меньше, чем кредитный лимит Предприятия в 1 00 млн .

тавляет компании А покрыть дополнительную долю. Эти инвестиционные решения ОТНа этот раз компания В решает не участвовать в выборке средств по кредиту и предос­

–  –  –

Когда заеrvпцик выплачивает КредИ'l', возвращенные средства распределяются между Аналогично, и процентные выплаты будут делиться между кредиторами пропорцио­ кредиторами в соответствии с их долями в фактическом Кредите, а не в ПредприJF.1lИИ .

н ал ьно долям в Кредите .

–  –  –

другой стороны, когда заемщик вносит плату за привилегию иметь доступ к Пред ­ с прИЯ'I'И1О, эти средства делятся пропорционально долям кредиторов в Предприя:тии не ­ зависимо от того, кто фактически дал деныи в кредит. Сам Кредит этими выплатами не изменяется. Существуют даже сценарии, в которых кредиторы перераспределяют плату за пользование независимо от процентных выплат .

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

–  –  –

Рис. 8.8. Модель кредита с использованием Распределения долей (Share Pi e) Б этих моделях больше не было специализированных объектов для долей Предпри­ ятия или Кредита. И те, и другие сведены в более наглядное Распределение долей ( Share Pie) 1. Такое обобщение позволяет ввести общую "арифметику долей", что очень сильно упрощает вычисление долей в любой транзакции и делает подобные расче­ ты более выразительными, лаконичными и легко комбинируемыми .

Но главное, из-за чего исчезли все преДЫДУlцие проблемы, - это устранение неуместно­ го ограничения в модели. ДOJIИ в Кредите больше не были привязаны к Долян 1 Pie (дословно "пирог") означает круговую диаграмму, на которой показано соотношение до­ лей в каком-либо общем количестве. Прu.меч. перев.

–  –  –

Надобность в понятии Вклада :в :кредИ'Z' (Loan Inves tment) тоже отпала, и в больше не требовались, и это устранило немалую часть специализированных проверок .

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

И вдруг оказалось, что на основе нашего нового видения предмета мы можем выпол­ нять любой рабочий сценарий, который нам встречался, практически без усилий и гораз­ 1lяmllы до проще, чем раньше. Что касается схем наших моделей, то они стали совершенно nоспециалистам, тогда как раньше те жаловались на "излишне теX1-lические" схемы .

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

Наша новая модель работала хорошо. Даже отлично .

А мы чувствовали себя отвратительно!

Тре звое решение Разумно было бы полагать, что в этот момент нам стало легче. Но это было не так .

Для проекта были установлены жесткие временные рамки, и мы уже находились в цейт­ ноте. Главным чувством, которое мы испытывали, был страх .

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

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

Именно в это время у нас состоялась встреча с руководителем проекта, которую я ни­ когда не забуду. Наш руководитель был человек умный и решительный. Он задал нам несколько вопросов .

В.: Сколько потребуется времени, чтобы вернуться к полному текущему набору рабочих функций, но в новой архитектуре?

о. : Примерно три недели .

В. : Можно ли решить наши проблемы без этого?

о. : Возможно, но никакой гарантии дать нельзя .

В. : Можно ли будет продвинуться дальше в следующей версии программы, если не сделать этого сейчас?

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

–  –  –

лучше соответствует цели nроекта. Если брать дальнюю перспективу, то сделать все по­ новому менее рискованно .

Тогда руководитель дал нам "добро" и сказал, что организационные вопросы он ре­ шит. Я неизменно восхищаюсь той смелостью и доверием к нам, с которыми он принял это решение. Мы закусили удила и сделали все за три недели. Работы было очень много, но прошло все на удивление гладко .

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

По мере разработки второй версии Распределение долей (Share Pie) стало унифИЦИРУЮIЦИМ фактором всего приложения. Как программисты, так и специалисты по финансам пользовались этим понятием при обсуждении системы. Маркетологи с его помощью обьяс1-lЯЛИ nотенциальным 1Ulиентам возможности nрограм.м,ы. А клиенты "подхватывали" понятие вслед за ними и включались в обсуждение программы. Так о н о вошло в ЕДИНЫЙ язык проекта, поскольку выражало самую суть такого явления, как синдицированные банковские кредиты .

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

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

К онц е нтрация на основах Не пытайтесь насильно вызвать качественный скачок, это только парализует работу .

Потенциал для скачка обычно возникает после ряда небольших рефакторинговых моди­ фикаций. БОЛЬШУIО часть работы занимают мелкие фрагментарные изменения, и пони­ мание модели тоже растет постепенно в ходе каждого последовательного улучшения .

Чтобы подготовить почву для скачка, сосредоточьтесь на пере работке знаний и куль­ тивировании надежного ЕДИНОГО ЯЗЫКА. Нащупывайте важнейшие понятия предмет­ ной области и в ы ражайте их в модели в явном виде (см. главу 9). Улучшайте архитекту­ ру программы, делая ее более гибкой (см. главу 10). Выполняйте дистилляцию модели (см. главу 1 0). Работайте с этими верными средствами, УЛУЧIIlайте понимание и нагляд­ ность - все это обычно предшествует качественному скачку .

1 88 ЧАСТЬ 1 1 1. УГЛУБЛЯЮЩИЙ РЕФАКТОРИНГ Не чурайтесь скромных, небольших изменений, которые постепенно улучшают мо­ дель, даже если они ограничены одной и той же общей концептуальной средой. Не сто­ порите работу, заглядывая слишком далеко вперед - просто не теряйте бдительности, чтобы не упустить шанс .

–  –  –

ния на Транэажции (Transac t i ons) стали формулироваться легко и точно Рис. 8.9. Еще один качественный скачок в модели, последовавший через несколько недель. Ограниче­ те), мы ВЫIlIЛИ на очередной уровень усовершенствования модели. В новой модели неявПроделав работу, аналогичную описанной выше (хотя, к счастью, и не в таком цейтно­

–  –  –

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

Как это часто бывает после настоящего качественного скачка к усовершенствованной модели, простота и наглядность новой архитектуры в сочетании с улучшенным качест­ вом коммуникации, благодаря новому ЕДИНОМУ ЯЗЫКУ опять привели к еще одному ка­ чественному скачку в моделировании .

В результате мы ускорили темп разработки программы на том этапе, на котором большинство проектов вместо ускорения тонет, как в болоте, в сложности и объеме всего написанного ранее .

–  –  –

У глубленная модель - это звучит здорово, но как ее построить? Углубленная мо­ ЦИИ, которые в сжатой и гибкой форме выражают важные знания о деятельности пользо­ дель обладает потенциалом, потому что содержит основные понятия и абстрак­ вателя, его проблемах и методах решения этих проблем. Первым шагом на пути к такой модели будет представление ключевых понятий предметной области в виде, пригодном для моделирования. Усовершенствование путем последовательных приближений, пере­ работки знаний и рефакторинга - все это будет позже. Но процесс только тогда получает и в архитектуру важное понятие (концепцию) из предметной области .

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

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

Извлеч ение понятий Разработчикам поневоле приходится вырабатывать чувствительность к признакам, за которыми скрывается наличие неявных понятий, а иногда даже активно выискивать та­ ковые. Большинство открытий этого рода происходит благодаря тому, что разработчики прислушиваются к употребляемому в группе языку, критически изучаlОТ "неуклюжие" места в коде и кажущиеся противоречия в утверждениях специалистов, перелопачивают литературу по предметной области, и к тому же очень много эксперимеНТИРУIОТ .

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

А потом на вас снисходит озарение. Название той совокупности данных, которая упо­ миналась в отчете, оказывается, обозначает важное понятие предметной области .

В крайнем возбуждении вы делитесь со специалистами своими открытиями. Они, соответ­ ственно, испытываIОТ облегчение, что вы, наконец, пришли к этому, или зевают от скуки, поскольку давно считали это само собой разумеющимся. В любом случае вы начинаете чер­ тить на доске схемы моделей вместо того, чтобы показывать ваши идеи на пальцах, как де­ лали раньше. Пользователи поправляют вас в деталях новой модели, но вы уже ясно види­ те, как изменилось качество обсуждения. Теперь вы с пользователями лучше понимаете друг друга, и демонстрация различных взаимодействий в модели для прохождения тех или иных рабочих сценариев стала более естественной. Язык модели предметной области стал более МОIЦНЫМ. Выполняя над кодом рефакторинг для реализации новой модели, вы обна­ Слушайте язык, на котором говорят специалисты предметной области. Есть ли руживаете, что архитектура программы стала более четкой и ясной .

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

Это отнюдь не старый подход "делаем все существительные объектами". Здесь вы Iцаясь и перерабатывая знание с целью выкристаллизировать четкое и полезное понятие .

слышите новое слово, и оно становится ориентиром; вы идете в новом направлении, об­ Когда пользователи программы или специалисты в предметной области пользуются лек­ сиконом, которого нет в модели, - это тревожный знак. А еlце более тревожный - когда термины, ОТСУТСТВУlощие в модели, употреБЛЯIОТ как разработчики, так и специалисты .

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

Пример

Пропущенное понятие в модели ГРУЗ0п о ставок Итак, разработчики написали работаlощее приложение, которое могло заказать дос­ тавку груза. Дальше они приступили к разработке приложения "операционной поддерж­ ки", в задачу которого входила помощь при манипулировании нарядами-заказами на по­ грузку и разгрузку в начале и конце маршрута, а также при перемещениях груза с одного транспортного средства на другое .

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

Давайте подслушаем фрагмент разговора (в сильно сокращенном виде) между про­ граммистом (п.) и специалистом по грузоперевозкам (С.) .

в таблице t'заказов на доставку груза" (cargo_bookings) .

п. Хочу проверить, все ли нужные для операционного приложения данные находятся

–  –  –

ция находится в таблице сейчас?

Потребуется полное описание пути следования Груза (Cargo). Какая информа­ с .

п. Идентификатор груза, рейс, место погрузки и место разгрузки для каждого участ­ ка пути .

с. А дата? Она понадобится операционному персоналу, чтобы договариваться об опе­ рациях перемещения груза .

п. Дату можно взять из расписания рейса. У нас нормализованная таблица .

с. Да, это нормальная практика - знать эту дату. Пути следования груза использу­ ются операционным персоналом для планирования предстоящих перемещений грузов .

п. Ну да... Ладно, доступ к датам у них точно будет. Программа управления опера­ циями сможет выдавать полную последовательность погрузок и разгрузок с датой каж­ дой манипуляции, т.е. то, что вы вроде бы называете "путем следования" .

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

п. По-моему, это делается просто для отчета. Основой для операционного приложе­ ния это сделать нельзя .

[ П о гра.м.мuсm задумывается, потом его осеняет.] р п. Так ведь путь следования - это на самом деле связующее звено между заказом груза и операциями над ним l с. Да, и еще это определенная связь с клиентом .

п. [Набрасывает схему на доске.] Это должно быть что-то в этом роде?

с. Да, в основном это правильно. Для каждого участка надо знать рейс, место погруз­ п. Итак, как только мы создали объект Участок IIY'1'И (Leg), он может извлекать данные ки И выгрузки, время .

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

–  –  –

п. О, кстати! Мы можем сделать так, чтобы интерфейс Службы маршрутизации эти данные в таблицу базы, и системе маршрутизации вообще не нужно будет знать ни­ (Routing service) возвращал объект пути следования. Тогда не надо будет помещать чего о таблицах .

с. Как-как?

п. Я имею в виду, чтобы система маршрутизации просто возвращала Путь следо ­ вания. Тогда его может сохранить в базе данных программа заказа доставки грузов в процессе сохранения остальных данных заказа .

с. А что, сейчас это делается не так? !

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

–  –  –

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

Итак, программист достаточно внимательно слушал специалиста по перевозкам, так что ему удалось заметить важность понятия "путь следования". Фактически все нужные для него данные уже собирались в программе в одно место, да и операционная часть объек­ та уже подразумевалась в отчете, но только введение в модель явного объекта Путь сле ­ дoBaHия открыло новые возможности .

Преимущества рефакторинга для нового объекта Путь следования ( I tinerary) таковы .

Более выразительное определение интерфейса Службы маршрутизации (Routing 1 .

Service) .

Независимость Службы маршруrzwизации от таблиц баз данных с заказами .

2 .

Более четкое оформление связи между программой заказа на доставку и программой 3 .

операционной поддержки (совместное использование объекта Путь следования) .

Снижение дублирования данных и операций, поскольку Пуrzwь следования опре­ 4 .

деляет время разгрузки/погрузки как для отчета о заказе, так и для программы

5. Удаление предметной логики из отчета о заказах и передача ее на изолированный управления операциями с грузами .

уровень предметной области .

Расширение ЕДИНОГО ЯЗЫКА, позволяющее более точное обсуждение модели и ар­ 6 .

хитектуры между программистами и специалистаи по перевозкам, а также между самими программистами .

В ыявление узких мест Нужные понятия не всегда сами всплывают на поверхность в дискуссиях или доку­ ментации. Бывает, что приходится копать глубже и проявлять изобретательность. А ко­ пать обычно нужно в самых "неуклюжих", неудобных местах программной архитекту­ ры - там, где процедуры делают сложные вещи, которые трудно объяснить; там, где при реализации каждого нового требования сложность постоянно возрастает .

Иногда бывает трудно даже понять, что в модели пропущено то или иное понятие .

Броде бы имеющиеся объекты справляются со всеми обязанностями, и только некоторые из них выполняются "неуклюже". Или же ощущается нехватка чего-то важного, но вот решение проблемы в модели никак не дается .

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

Пример Вычис ление процентов: трудный способ Следующая история происходит в гипотетической финансовой компании, которая вкладывает средства в коммерческие кредиты и другие активы, приносящие проценты .

–  –  –

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

–  –  –

Ночной пакетный сценарий перебирает все Активы (As sets) и каждому отдает рас­ поряжение "рассчитать проценты от даты", c a l culate lnterestForDa t e ( ), соответст­ вующей этому ДНIО. Сценарий получает вычисленное значение (доход) и передает его вместе с именем конкретной учетной книги в СЛУЖБУ, которая представляет public­ интерфейс программы бухучета. Эта программа вносит заданную сумму в указаННУIО книгу. Аналогичным образом сценарий перебирает все Активы, собирая с них получен­ ные за день платежи, и помещает цифры в другую книгу .

Один из программистов все это время боролся 1 с возрастаЮlцей сложностыо вычис­ ления процентов. Он начал подозревать, что должна существовать модель, лучше при­ способленная к этой задаче, и пригласил облюбованного им специалиста по предметной области, чтобы тот помог ему покопаться в проблемной части приложения .

Программист (П.). Наш КалЬКУЛЯ:ТОР процентов ( Intere s t Calcul ator) ведет себя все хуже и хуже .

Специалист (С.). Это сложная часть работы. У нас есть много случаев, которые да)ке еще не реализованы .

п. Я это знаю. Мы можем добавить новые виды нроцентных доходов, заменив Каль ­ КУЛЯ:ТОР процентов. Но сейчас главная проблема состоит в другом - во всех этих осо­ бых случаях, когда про центы не платятся по графику .

С. Ничего такого "особого" в этих случаях нет. График платежей устроен очень гибко .

п. Когда в свое время мы выделили КалЬКУЛЯ:ТОР процентов из Актива, это очень помогло. Нам нужно и дальше разбивать его .

1 У автора в примерах с программным обеспечением для учета дохода от финансовых активов фигурирует женщина-программист употребляется местоимение "она". Возможно, это следова­ ние принципу политкорректности. Но в русском переводе это создает определенные неудобства, в отличие от английского, где разница только в местоимении. А так как этот пример гипотетиче­ ский, я счел возможным заменить гипотетическую женщину на гипотетического мужчину­ программиста. - flрuмеч. перев .

ЧАСТЬ 1 1 1. УГЛУБЛЯЮЩИЙ РЕФАКТОРИНГ Хорошо .

с .

тут подумал: может быть, вы описываете вычисление процентов в каком-то сво­ п. я ем стиле .

с. Что вы имеете в виду?

п. Ну, например, мы в программе отслеживаем подлежащий погашению процент, не уплаченный на протяжении расчетного периода. У вас есть для него какой-нибудь термин?

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

п. Так вам эта цифра не нужна?

с. Ну, иногда мы можем на нее взглянуть, но вообще мы не так ведем дела .

п. Ладно, так если платеж и процент - это разные вещи, может, нам нужно и модели­ ровать их отдельно? Что скажете, например, об этом? [Рисует на доске.]

–  –  –

Вроде нормально. Но вы просто перенесли его из одного места в другое .

с .

Да, но теперь КалЬКУЛЯ:'Z'ОР процен'Z'ов ( Intere s t Calculator) отслеживает п .

только начисленные проценты, а Пла'Z'еж (Payment) содержит отдельное число. Особого упрощения это не дает, но как будто лучше отражает вашу деловую практику, разве нет?

с. А, понятно. Кстати, можно нам иметь и историю начисления процентов тоже?

Аналогично Ис'Z'ории пnа'Z'ежей (Payment His tory) .

в исходную архитектуру .

п. Да, у нас уже запрашивали эту новую возможность. Но ее можно было бы добавить с. Вот как. ЧТО ж, когда я увидел такое разделение между Ис'Z'орией пnа'Z'ежей и процентов, я подумал, что вы разделяете проценты, для того чтобы организовать их аналогично ИС'Z'ОРИИ пnа'Z'ежеЙ. Бы что-нибудь знаете об учете по методу начислений (ассrnаl basis ассоиnting) ?

Объясните, пожалуйста .

п .

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

п. Так вы говорите, что если вести список "начислений" (accrnals), то можно взять итог или... "провести" их по мере необходимости?

с. Провести, вероятно, по дате начисления, но взять итоговую сумму да, в любой момент. Комиссионные сборы (fees) устроены так же, но проводятся, конечно, через дру­ гую книгу .

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

–  –  –

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

п. Вы сказали, комиссионные сборы устроены таким же образом? Но они... как вы там говорили... проводятся по другой книге?

–  –  –

п. Б этой модели у нас вычисление процентов или, скорее, логика расчета 'начисле­ ний из Калькуля:тора процентов отделена от их учета. И до сих пор я не замечал, как много дублирования в Калькуля:торе сборов (Fee Cal culator). А теперь можно с. Да, вычисления и раньше выполнялись правильно, но теперь я хорошо вижу, ЧТО легко добавлять различные виды сборов .

происходит .

Поскольку классы Калькуляторов не были непосредственно связаны с другими частями архитектуры, рефакторинг оказался довольно простым в исполнении. Разработчик смог пе­ реписать модульные тесты с использованием нового языка за несколько часов, и новая архи­ тектура заработала к концу следующего дня. В конце концов, у него получилось вот что .

Б новой версии приложения (после рефакторинга) ночной пакетный сценарий при­ lateAccrua l sThroughDate ( ). При этом возвращается коллекция НачислеНИЙ казывает каждому Ах-zwиву (As set) выполнить "расчет начислений по дату", calcu­ (Accrua l s), и каждая сумма заносится в указанную книгу учета .

–  –  –

Новая модель имеет ряд преимуществ. Результаты внесенных изменений .

Единый язык обогатился термином "начисление" (accrual) .

1 .

Начисления отделены от фактических платежей .

2 .

3. Знание предметной области (например, по какой книге выполнять бухгалтерскую проводку) перемещено из сценария в изолированный уровень прикладной модели .

4. Проценты и сборы сведены вместе таким образом, который соответствует факти­ ческому ведеНИIО дел и устраняет дублирование в коде .

5. Создана возможность легко добавлять новые варианты процентов и сборов в виде Графиков начисnеНИЙ (Ассruаl Schedu l e s ) .

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

К счастью, у разработчика бьш толковый и заинтересованный партнер-специалист по бан­ ковскому делу. Если бы знания приходили из более пассивноm источника, ему пришлось бы отсечь больше тупиковых направлений и привлечь других разработчиков для совместного "мозmвого штурма". Это замедлило бы прогресс в работе, хотя и не остановило бы его .

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

Как-то раз астроном Галилей сформулировал парадоксальное утверждение. Наши чувства ясно свидетельствуют, что Земля стоит на месте: людей не сдувает с нее, они не уносятся прочь. Тем не менее Коперник убедительно показал, что Земля движется во­ круг Солнца с большой скоростью. Разрешение этого противоречия порождает важней­ шее знание о законах природы .

Галилей провел мысленный эксперимент. Если всадник уронит шар со скачущей ло­ шади, куда тот упадет? Конечно, шар будет двигаться вместе с лошадью, пока не упадет ГЛАВА 9. П ЕРЕВОД НЕЯВНЫХ ПОНЯТИЙ В ЯВНЫЕ 1 99 на землю ей под ноги, как если бы лошадь стояла на месте. Из этого Галилей вывел пер­ воначальное понятие об инерционных системах отсчета, разрешив парадокс и выработав гораздо более полезную модель физики движения .

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

Все противоречия, как правило, на практике устранить не получается, да это и не все­ гда бывает желательно. ( В главе 1 4 подробно рассматривается, как принимать решения по этому поводу и что делать с результатами.) Но даже если противоречие остается пе­ устраненным, все равно бывает полезно обдумать, как два противоположных утвержде­ ния могут быть справедливыми в отношении не коей внешней реальности .

Чтение книг Разыскивая подходящие понятия для моделей, не пропустите очевидного. Во многих областях знания существует литература, рассказ ы ваЮIЦая об основных понятиях и необхо­ димых общих знаниях по предмету. Конечно, придется поработать и с собственными сне­ циалистами, чтобы выделить ту часть знания, которая непосредственно относится к про­ блеме, и переработать ее в нечто пригодное для реализации в объектно-ориентированной программе. Но тем не менее будет нелишпим начать со связного и глубоко продуманного взгляда на предмет, изложенного в литературе .

П ример Вычисление проц ентов : и зучаем литературу Представим себе другой сценарий для программы отслеживания кредитных капитало­ вложений, которая рассматривалась в ПрОlIIЛОМ примере. Как и раньше, история начинает­ ной, особенно КалЬКУЛЯТОР процентов (Interest Calcul ator). Но в этом случае ся с того, что программист понимает: архитектура программы становится все более неудоб­ обязанности специалиста по предметной области не имеют никакого отношения к пробле­ ответственно, программист не может обратиться к снециалисту и вместе с ним "нащупать" ме, и ему нет интереса помогать в разработке программного обеспечения как таковой. Со­ недостаIощие понятия, которые, по-видимому, прячутся где-то под поверхностыо .

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

тыal0mm в момент его начисления, да:же если он еще не выlлачен•. Все uэдеРJICКИ Метод начислении при учете доходов и издержек. В этом методе доход учи­ таЮlCе учитываl0тся в момент начисления, будь то фактическая их Bblrulama Wlи выlтавлениеe счета к оплате. Лl0бые долговые обязательства, 61UllОЧая на­ логи, проходят по статье расходов.2 Теперь наш разработчик больше не должен заново изобретать бухгалтерский учет .

Еще немного обсудив вопрос с другим программистом, он приходит к такой модели .

образования и ученой степени". Изд-во Adams Media, 2000 г. (Finance and Accounting: How to Кеер Your 2 Каплан с. "Финансы и бухгалтерский учет: как вести учет своих финансов, не имея специanьного Books and Manage Your Finances Without аn МВА, а еРА or а Ph.D., Ьу Sllzannc Caplan, AdaIns Media, 2000) .

–  –  –

Рис. 9.9. Слеz'/(а углубленная моделъ, построенная на основе информации из '/(llИZU Он еще не понял, что генераторами дохода ЯВЛЯIОТСЯ Активы (As sets), поэтому в модели все еще ПрИСУТСТВУIОТ Калькуляторы. З нание о книгах учета находится на опе­ раuионном уровне вместо уровня предметной области, где оно более уместно. Н о он уже отделил фактический платеж от начисления дохода, что было самым проблематичным местом в модели. В модель и в ЕДИНЫЙ ЯЗЫК введено слово " начисление " (accrual) .

Позднее можно будет в итерационном режиме внести дальнейшие усовершенствования .

Когда разработчик наконец смог поговорить со специалистом по финансам, тот был приятно удивлен - впервые программист проявил интерес к его профессии. Ввиду осо­ б енностей распределения обязанностей им никогда раньше не приходилось вместе си­ деть над моделыо, как ЭТО было в предыдущем примере. Но поскольку приобретенные этим программистом познания позволили ему задавать более грамотные вопросы, спе­ цианист стал более внимательно прислушиваться к нему и постарался отвечать на во­ просы быстрее и точнее .

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

Еще один вариант, который есть у программиста ЭТО почитать публикации других

---профессиональных программистов, имеющих опыт разработки программного обеспече­ ния для нужной предметной области. Например, глава 6 из [ 1 1] могла бы направить на­ шего программиста в совершенно другом направлении, не обязательно ЛУЧIllем ИЛИ худ­ шем. Из такой литературы вряд ЛИ можно почерпнуть готовое решение. Скорее, она дала бы программисту несколько отправных точек для его собственных экспериментов, а так­ же сводный опыт предшественников, уже побывавших на этой территории. И тогда не пришлось бы заново изобретать велосипед. Э тот вариант рассматривается более подроб ­ но в главе 1 1 .

ГЛАВА 9. ПЕРЕВОД НЕЯВ НЫХ ПОНЯТИЙ В ЯВН Ы Е

Метод проб и ошибок Приведенные здесь примеры не передают в ПОЛНОЙ мере того, сколько проб и ошибок нужно сделать на пути к успеху. Я в дискуссии вылавливал, наверное, с полдюжины разных подсказок, прежде чем попадалась одна достаточно ясная и полезная, с КОТОРОЙ можно бы­ ло поработать на модели. Но потом даже этот ключ к решению минимум один раз прихо­ дилось заменять другим, по мере того как накопление опыта и переработка знаний подска­ зывали новые идеи. Тому, кто занимается моделированием и проектированием программ­ ных систем, нельзя слишком привязываться к своим собственным идеям .

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

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

Моделиров ание неочевидных понятий ()бъектно-ориентированная парадигма требует от нас искать и изобретать понятия определенного вида. Плотью большинства объектных моделей являются реально суще­ ствующие вещи, пусть даже такие абстрактные, как бухгалтерские " начисления ", а также действия, предпринимаемые этими вещами. Это и есть " существительные и глаголы", о которых написано в ЛIобой вводной книге по объектно-ориентированному проектиро­ ванию программ. Н о в модель можно также ВКЛIОЧИТЬ явно определенные понятия, отно­ сящиеся и к другим категориям .

Здесь мы рассмотрим три такие категории, которые для меня не были очевидными, когда я начинал работать с объектами. По мере того как я знакомился с ними, мои проек­ ты становились все качественнее .

Явные условия-ограничения у словия-ограничения образуют особо важную категорию понятий модели. ()ни часто сущеСТВУIОТ в неявном виде, и перевод их в явную форму может сильно улучшить КОН­ цептуальную архитектуру .

Иногда ограничения естественным образом вписываIОТСЯ в какой-нибудь объект или метод. Так, объект "В едро " (Bucket) должен гарантировать соблюдение слеДУIощего ИН­ варианта: он не должен содержать б ольше вещества, чем позволяет его емкость .

–  –  –

Логика этого ограничения настолько проста, что вводимое правило тривиально. Од­ нако его так же легко и потерять в более сложном классе. Давайте выделим его в отдель­ НЫЙ метод под именем, которое четко и в явном виде выражало бы смысл накладываемо­ го ограничения .

c las s Bucket { private f l oat capac i tY i private f l oat content s i

–  –  –

в обеих версиях кода проверяется и форсируется соблюдение ограничения, но во втором присутствует более явная связь с моделью (что есть основное требование ПРОЕКТИРОБАНИЯ ПО МОДЕЛИ). Это простое правило было вполне понятно и в исходной форме, но при проверке соблюдения более сложных правил они начинают загромождать об ъект или операцию, к которым ПРИl\1еняются, как это происходит с ЛIобым неявно вво­ димым понятием. В ыделение условия-ограничения в собственный метод позволяет дать ему информативное имя, благодаря которому ограничение становится явным и замет­ ным в архитектуре программы. Теперь это уже предмет с именем, КОТОРЫЙ можно обсу­ ждать. Такой подход также предоставляет ограничению достаточное смысловое про­ странство. Б олее сложное правило запросто может разрастись в метод более "длинный ", чем тот, который его вызывает (в нашем случае метод pourI n ( ) ). Тогда вызываIОЩИЙ метод остается простым и фокусируется на своих прямых обязанностях, а сложность ог­ раничения может возрастать как угодно по мере надобности .

Отдельный метод дает условию-ограничению некоторое пространство для роста, но бывает много случаев, когда связь просто нельзя " скомпоновать " в один метод. Или же, даже если метод остается простым, он может требовать для себя информацию, которая объекту не нужна для выполнения его основных обязанностей. То есть, правило может элементарно не вписываться ни в один сущеСТВУIОЩИЙ объект .

ГЛАВА 9. ПЕРЕВОД НЕЯВН Ы Х ПОНЯТИЙ В ЯВНЫЕ

В от несколько тревожных свидетельств о том, что условие-ограничение искажает ар­ хитектуру объекта, в котором она инкапсулирована .

1. Для проверки ограничения требуются такие данные, которые б ез него излишни для определения объекта .

2. В нескольких объектах фигурируют связанные между собой правила, из-за чего приходится вводить ду блирование или наследование объектов, тогда как при ОТ­ сутствии этих правил они не являются родственными .

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

К огда ограничения " затемняют " основные обязанности объекта или когда ограниче­ ние играет важную роль в предметной области, но не в модели, его можно выделить в ОТ­ дельный объект или даже промоделировать в Биде набора объектов и отношений .

(Подробно и не слишком формализовано о б этом говорится в [23] ) .

Пример

Краткий обзор: избыточное резервирование главе 1 мы встречались с о б ычной деловой практикой при грузоперевозках - ре­ в зервированием на 10% б ольше груза, чем его может перевезти транспортное средство .

( О пыт научил фирмы, занимаIощиеся поставками и перевозками, что этот избыток ком­ пенсируется отменами заказов в последний момент, так что их транспорт уходит практи­ чески полным.) Э то условие-ограничение на ассоциацию между Рейсом (Voyage) и Грузом (Cargo) б ыло сформулировано в явном виде как на схемах, так и в коде, путем добавления нового класса, представляющего само ограничение .

–  –  –

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

Я говорю здесь о процессах, которые существуют в предметной о бласти и которые мы обязаны представить в модели. К огда таковые возникают на практике, они имеlОТ тен­ денцию приводить к " неуклюжим " объектным архитектурам .

Первый пример в этой главе посвящен системе управления грузопоставками, которая маршрутизировала грузы. Процесс маршрутизации это нечто, имеIощее смысл в предЧАСТЬ 111. УГЛУБЛЯЮЩИЙ РЕФАКТОРИНГ метной области. Одним из способов выразить это нечто является СЛУЖБА ( SERVICE), ко­ торая мо)кет инкапсулировать очень сложные алгоритмы .

Если существует несколько способов выполнения того или иного рабочего процесса, возникает идея сделать сам алгоритм или некую его ключевую часть отдельным объек­ том. Тогда выбор между процессами становится выбором между объектами, каждый из которых представляет отдельную СТРАТЕГИЮ (STRATEGY). (Использование СТРАТЕГИЙ в предметной области более подробно рассматривается в главе 12.) Основное отличие между процессом, который следует сделать отдельным объектом, и процессом, которы й должен быть скрыт " в недрах" кода, состоит вот в чем: говорят ли об этом процессе специалисты по предметной области, или он составляет не более чем часть внутреннего механ изма программы?

Условия-ограничения и процессы - это две широкие категории понятий моделей, ко­ торые далеко не всегда приходят на ум при программировании на объектно­ ориентированных языках. А между тем они могут сделать архитектуру более четкой бла­ l'одаря тому, что мы начнем думать о них как об элементах модели .

Некоторые полезные категории понятий имеют более узкий характер. В завершение этой главы я расскажу об одной намного более специфической, хотя и вполне обычной, категории. Для выражения некоторых разновидностей правил или регламентов служат СПЕЦИФИКАЦI1И (SPECIFICATIONS), которые высвобождают эти правила в явном виде из системы условных проверок и помещают в модель на видное место .

Я разработал шаблон СПЕЦИФИКАЦИЯ в сотрудничестве с Мартином Ф аулером [9] .

Обманчивая прuстота самого понятия сопровождается многими тонкостями на операци­ онном уровне и в реализации, так что в этой главе будет много подробностей. Еще боль­ ше на эту тему будет говориться в главе 10, где этот шаблон будет дополнен. О знако­ мившись с введением в шаблон, идущий далее раздел " Применение и реализация специ­ фикаций " можно пока что прочитать бегло и отложить до тех пор, пока у вас не возникнет потребность в практическом применении этих знаний .

–  –  –

Во всех программах встречаlОТСЯ логические (булевы) проверки, которые фактически являются элементами несложных регламентных правил. Пока такие правила достаточно просты, их можно реализовать с помощыо проверочных методов, например, anI terator .

hasNext ( ) ( счетчикЦикла.имеетСледующееЗначение) или anInvoice. I sOver­ due ( ) (счетФактура. просрочен). В классе Счет-фактура (Invoice) код, помещен­ ный в метод i sOverdue ( ) (просрочен), реализует алгоритм проверки некоторого прави­ ла и выдачи ответа в виде логического значения .

–  –  –

Но не все правила так просты. В том же классе Сче'Z' - фактура (Invoice) может при­ сутствовать еще один метод-правило, anInvoice. i sDel inquent ( ) (счетФактура. не­ Оплачен). Э тот метод, вполне возмо)кно, начинается с проверки просроченности счета, но ГЛАВА 9. ПЕРЕВОД Н ЕЯВН Ы Х ПОН ЯТИ Й В ЯВН Ы Е 205 не заканчивается на ней. Длительность предоставляемого срока оплаты, например, может зависеть от состояния банковского счета клиента. По некоторым неоплаченным счетам­ фактурам должно высылаться второе извещение, а другие счета должны передаваться в коллекторское агентство. А еще есть кредитно-платежная история клиента, политика компании по разным видам услуг и товаров... и вот уже очевидны й смысл объекта Счет­ фаКlX'ура как запроса на оплату теряется в массе кода, занимающегося проверкой правил .

Кроме того, класс СчеlX'-фактура быстро обрастает всевозможными связями с классами и подсистемами прикладной модели, которые также не подчеркивают его основной смысл .

В попытках спасти класс Счет-фаКlX'ура (Invoice) на этом этапе его существования программист, скорее всего, выполнит рефакторинг кода проверки правил, чтобы вывести его на уровень прикладных операций (в данном случае - в систему инкассирования). Та­ ким образом правила окажутся вооб ще отделенными от уровня предметной области, а в нем останутся только мертвые объекты данных, вооб ще не выражающие базовые законо­ мерности и деловые регламенты прикладной модели. Такие правила должны оставаться на уровне предметной области, но их проверка неуместна в самом проверяемом объекте (в данном случае это СчеlX' -фактура). К тому же, проверочные методы быстро обрастают всевозможным кодом контроля условий, отчего правила становится трудно читать .

Разраб отчики, придерживающиеся парадигмы логического программирования, по­ шли б ы по другому пути. О ни б ы выразили правила в виде предикатов. Предикаты - это функции, выдающие значения " истина" или " ложь ", которые можно ком б инировать с помощью таких операций, как И и или для выражения б олее сложных правил. С по­ мощью предикатов можно было б ы объявлять правила непосредственно и применять их к о бъекту типа Счет -фактура. Ах, если бы мы работали в парадигме логического про­ граммирования.. .

С учетом всего этого кое-кто попытался применить логические правила в системе, основанной на о бъектах. Н екоторые из таких попыток были весьма искусными, другие же - наивными. Одни метили высоко, другие ограничивались малым. Н екоторые дали результат, но многие б ыли отброшены как провальные эксперименты. Результатом не­ скольких б ыл срыв ряда проектов. Ясно только одно: несмотря на всю привлекатель­ ность идеи, полная реализация логики в о бъектах - очень непростое предприятие .

( В конце концов, логическое программирование само по себ е - целая парадигма моде­ лирования и построения программно й архитектуры.) Реализация правил прикладной модели (деловых регламентов) часто не вписывает­ ся в обязанности никаких из лежащих на поверхности СУЩНОСТЕЙ или ОБЪЕКТОВНАЧ ЕНИЙ, а их разнообразие и обилие возможных комбинаций может исказить ос­ новной смысл объекта предметной области. Но " вынесение " правил из уровня пред­ метной области - еще хуже, поскольку в таком случае код этого уровня перестает вы­ ражать модель .

Логическое программирование предлагает концепцию отдельных комбинируемых бъектов-правил, именуемых " предикатами ", но полная реализация этой концепции о с помощью объектов довольно громоздка. Она также имеет слишком оБЩИЙ характер, чтоб ы передавать смысл модели в коде так же хорошо, как более специализированные конструкции .

К счастью, для получения хорошего результата нам нет необходимости полностью реа­ лизовать парадигму логического программирования. Б ольшинство наших правил сводится к нескольким частным случаям. Мы можем заимствовать понятие предиката и создать спе­ циализированные объекты, выдающие логическое ( булево) значение. Те самые провероч­ ные методы, которые у пас выходили из-под контроля, прекрасно "заживут" в виде отдель­ ных объектов. О ни будут представлять собой небольшие тесты на истинность, которые ЧАСТЬ 111. УГЛУБЛЯЮЩИЙ РЕФАКТОРИНГ можно выделить в отдельные ОБЪЕКТЫ-ЗНАЧЕНИЯ. Н овый объект подобного рода будет оценивать другой объект, проверяя истинность применяемого к нему предиката .

–  –  –

Другими словами, этот новый объект будет спецификацией. СПЕIIИФИКАЦИЯ (SPECI­ FICATION) задает условие-ограничение на состояние другого объекта, которое может воз­ никнуть, а может и не возникнуть. Применений у нее много, но наиболее существенное СОСТОИТ в ТОМ, что СПЕЦИФИКАЦИЯ может проверять любой объект на соот­ по смыслу ветствие заданным критериям .

Создайте набор явно определенных, предикатоподобных ОБЪЕКТОВ-ЗНАЧЕНИЙ дЛЯ решения специализированных задач. СПЕЦИФИКАЦИЯ это предикат, который опре­ деляет, удовлетворяет о бъект некоторым критериям или нет .

М ногие СПЕЦИФИКАЦИИ являются простыми, узкоспециальными проверками, как в примере с неоплаченным счетом-фактурой. В случаях, когда предполагается реализа­ ция сложных регламентов, можно добавить возможность комбин ирования простых спе­ цификаций, как в случае комбинирования предикатов с помощью логических операций .

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

Случай неоплаченного счета-фактуры можно промоделировать с помощью СПЕЦИФИКАЦИИ, которая бы определяла, что такое " неоплаченный ", могла бы проверить любой объект Счет - фактура и выдать заключение о его неоплаченности .

–  –  –

Поэтому счет-фактура считается неоплаченным .

Рис. 9. 13. Более сложный регламент для определения неоnлаченности, выделенный в отдельную

СПЕЦИФИКАЦИЮ

При наличии СПЕЦИФИКАЦИИ правило-регламент остается на уровне предметной об­ ласти. Поскольку правило теперь является полноценным объектом, архитектура более чет­ ко отражает модель. ФАБРИКА может сконфигурировать СПЕЦИФИКАЦИЮ, используя ин­ формацию из других источников, - например, из банковского счета клиента или базы дан­ ных регламентов компании. Если предоставить доступ к этим источникам непосредственно

ГЛ АВ А 9. П ЕРЕ В ОД Н ЕЯВ НЫХ ПОНЯ ТИЙ В Я В НЫЕ

объекту Счет-фактура, это создаст между ними такую связь, которая непосредственно не относится к запросу на оплату (т.е. к непосредственной обязанности объекта). В этом случае необходимо создать объект Спецификация неопnаченного счета-фактуры (Delinquent Invoice Specification), использовать его для проверки тех или иных Счето:в-фактур, а затем ликвидировать, чтобы осталась только конкретная дата проверки - это упрощение реализ ации. А СПЕЦИФИКАЦИИ можно предоставить любую неоБХОДИМУIО для выполнения ее обязанностей информацию прямым и непосредственным способом .

*** Б азовое понятие СПЕЦИФИКАЦИИ само по себе просто и помогает сосредоточиться на проблеме моделирования предметной области. Но для П РОЕКТИРОВАI-IИЯ ПО МОДЕЛИ (MODEL-DRIVEN DESIGN) нужна еще и эффективная реализация, которая также выражает это понятие. Чтобы справиться с этим требованием, необходимо подробнее разобраться с новым архитектурным шаблоном. Шаблон -- это не просто технически й прием для вы­ черчивания U МL-диаграмм; это решение проблемы программирования, которое к тому же следует принципам ПРОЕКТИРОБАНИЯ ПО МОДЕЛИ .

Если вы примените шаблон правильно, перед вами раскроется целая стратегия реше­ ния определенного класса задач, относящихся к моделированию предметной области, и вы сможете черпать информацию из сокровищницы знаний, созданной годами пuиска эффективных реализаций. В слеДУIощем разделе подробно описываются различные ас­ пекты спецификаци й, дается много вариантов реализации тех или иных возможностей .

Архитектурный шаблон - это не книга готовых рецептов; он всего лишь позволяет на­ чать решение проблемы с готовой базы опытного знания и дает язык, на котором можно обсуждать проблему .

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

Применение и реализация спецификаций Ценность СПЕЦИФИКАЦИЙ в значительной мере состоит в том, что они сводят воеди­ но такие прикладные функции, которые могут показаться совершенно различными. Нам может понадобиться определить состояние некоторого объекта для одной или несколь­ ких следующих целей .

1. Проверить пригодность объекта для удовлетворения какой-то потребности или достижения какой-то цели .

2. В ыбрать объект из коллекции ему подобных (как в случае запроса на просрочен­ ные счета-фактуры) .

3. З аказать создание нового объекта для определенных потребностей .

Эти три области применения - проверка пригодности, отбор по образцу, создание по заказу ---- на концептуальном уровне предстаВЛЯIОТ собой одно и то же. Н е имея такого шаблона, как СПЕЦИФИКАЦИЯ, одно и то же правило можно было бы реализовать "в раз­ ных обличьях " - не исключено, что и противоречащих друг другу. Так было бы потеряно принципиальное единство содержания. Н о применение шаблона СПЕЦИФИКАЦИЯ позво­ ляет использ овать единообразную модель, пусть даже в разных формах реализации .

–  –  –

ГЛАВА 9. ПЕРЕВОД НЕЯ В НЫХ ПОНЯТИЙ В Я В НЫЕ

Отбор по запросу Проверка пригодности заКЛIочается в выяснении, соответствует ли некий конкретный объект заданным критериям - скорее всего, для того, чтобы по итогам проверки что-то предпринять. Но часто возникает и другая задача - выбрать подмножество из набора объек­ тов по некоторым критериям. Здесь применимо все то же понятие СПЕЦИФИКАЦИИ, но с другими особенностями реализации .

Предположим, от программы требуется выдать список всех клиентов с неоплаченны­ ми Счетами-фактурами ( Invoices). Теоретически тут применима та же СпеЦИфИI:а ­ ция неопnаченного счета-фактуры (De l inquent Invoice Specification ), которую мы определили раньше, но на практике придется несколько изменить реализа­ цию. Чтобы продемонстрировать, что идея осталась то й же, предположим для начала, что количество Счетов-фактур невелико и уже находится прямо в памяти. В этом случае пригодится упрощенная реализация, разработанная для проверки пригодности. В Хра ­ нилище счетов-фактур ( Invoice Reposi tory) можно включить обобщенны й ме­ тод, которы й бы отбирал Счета-фактуры по СПЕЦИФИКАЦИИ .

{ pub l i c Set select Sat i s fying ( I nvo i ceSpec i f i cat i on spec )

–  –  –

new De l inquent I nvo i ceSpec i f i cat i on ( currentDate ) ) ;

В этой строке кода четко передается концептуальны й смысл операции. Конечно, объек­ ты Счет -фактура, скорее всего, находятся не в оперативной памяти; их вообще могут быть многие тысячи. В типично й коммерческо й системе делопроизводства такая инфор­ мация обычно хранится в реляционной базе данных. А как уже говорил ось в предыдущих главах, в местах, где происходит пересечение с другими программными технологиями, есть шанс потерять концентраЦИIО на предметной модели .

В реляционных СУБД имеются мощные поисковые механизмы. Как же нам восполь­ зоваться их мощыо, чтобы эффективно решить проблему, но не потерять модель СПЕЦИФИКАЦИИ? В подходе ПРОЕКТИРОВАНИЯ ПО МОДЕЛИ требуется, чтобы модель "шла в ногу " с реализацией, но при этом разрешается свободно выбирать люБУIО реали­ зацию, которая честно передает смысл модели. К счастью для нас, язык SQL это очень естественный способ писать СПЕЦИФИКАЦИИ .

В от простой пример, в котором запрос инкапсулируется в том же классе, что и прове­ рочное правило. В Спецификацию счета-фактуры ( Invoice Specification) до­ бавлен один метод, который реализован в подклассе Спецификация неопnаченного счета-фактуры (De l inquent Invoice Spec ification) .

–  –  –

СПЕЦИФИКАЦИИ легко находят общий язык с ХРАНИЛИЩАМИ стандартными ме­ апросам к объектам предметной области, ханизмами для предоставления доступа по з в частности для инкапсуляции интерфейса баз данных (рис. 9. 15) .

Но теперь в этой архитектуре есть ряд проблемных мест. В ажнее всего то, что под­ роб ности с труктуры таблиц " проникли " на уровень предметной области, тогда как их следовало бы вывести в отдельный уровень отображения, устанавливающий соответст­ вие между объектами предметной области и реляционными таблицами. Неявное дубли­ рование этой информации именно здесь может помешать дальнейшей доработке и ис­ пользованию объектов Счет-фактура ( Invoice) и Cus tomer (Клиент), потому что лю бое изменение в их отображении теперь нужно отслеживать в нескольких местах. Но этот пример - просто иллюстрация к тому, как держать правило в одном месте. Некото­ рые среды для поддержки отображения между объектами и реляционными базами по­ зволяют выразить з апрос в терминах объектов и атрибутов модели, генерируя SQL-код самостоятельно на инфраструктурном уровне. Такими средствами можно убить сразу двух зайцев .

Но если инфраструктура не приходит на помощь, SQL-код можно выделить и з смы­ словых объектов предметной области, добавив специальный запросный метод в Храни ­ лище счетов-фактур ( Invoice Reposi tory). Чтобы не помещать регламентное

ГЛАВ А 9. П ЕРЕВОД НЕЯВНЫ Х ПОНЯТИЙ В ЯВН Ы Е

правило в само ХРАНИЛИЩЕ, нужно постараться выразить запрос более общим спосо­ бом - таким, который не содержит явно самого правила, но может комбинироваться и помещаться в контекст так, чтобы фактически построить это правило (в данном случае

–  –  –

pub l i c Set sat i s fyingE l ement sFrorn ( I nvo i c eRepo s i tory repo s i tory ) / /Правило нео п лаченно сти о п ределяет ся та к:

/ / "на текущую дат у срок оплаты ист ек" return repo s i t ory. s e l e c tWhe reGracePeriodPa s t ( currentDate ) ;

} } Код SQL помещается в ХРАНИЛИЩЕ, а СПЕЦИФИКАЦИЯ отвечает за то, какой именно запрос должен использоваться. Правила не так уж изящно собраны в эту СПЕЦИФИКАЦИЮ, но, тем не менее, здесь присутствует КЛIочевое определение, что такое неоплаченность счета (истечение срока его оплаты) .

Теперь в ХРАНИЛИЩЕ имеется очень специализированный з апрос, который, скорее все­ го, будет использоваться только в этом случае. Э то приемлемо, но в зависимости от количе­ ства просроченных Счетов-фактур по сравнению с неоплаченными может оказаться дос­ таточно эффективным промежуточное решение, в котором методы ХРАНИЛИЩА имеют бо­ лее общий характер, а код СПЕЦИФИКАЦИИ более нагляден .

–  –  –

этом коде достигается скачок быстродействия, поскольку одновременно извлекает­ Б ся б олып е С четов - фахт ур, после чего отбор из них выполняется в оперативной памяти .

Приемлемая ли это цена за лучшее разделение обязанносте й - это зависит целиком от конкретных обстоятельств. Реализовать взаимодействия между СПЕЦИФИКА ЦИЯМИ и ХР АНИЛИЩАМ И можно разными способами, используя специфические преимущества платформы разработки, но не трогая распределения основных обяз анностей .

Иногда для улучшения быстродействия или, скорее, для повышения безопасности запросы могут быть реализованы на сервере как хранимые процедуры. В этом случае СПЕЦИФИКА ЦИЯ может с одержать только параметры, разрешенные в такой процедуре .

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

ГЛАВА 9. П ЕРЕ В ОД НЕЯ ВНЫ Х ПОНЯТ ИЙ В Я ВН Ы Е 213 В се вышеизложенное лишь слегка затронуло пласт проблем, которые связаны с орга­ низацией связи между базами данных и СПЕЦИФИКАЦИЯМИ, и я не буду даже пытаться охватить все соображения, которые при этом возникают .

Я хочу лишь дать самое общее понятие о том, како й выбор приходится делать в таких случаях. Н екоторые технические вопросы, связанные с разработко й ХРАНИЛИЩ со СПЕЦИФИКАЦИЯМИ, рассмотрены Ми ( Мее) и Хай еттом (Hieatt) в книге [ 13] .

Создание по заказу Когда Пентагон желает получить новы й самолет-истребитель, его сотрудники разра­ батывают техническое задание - спецификацию. В спецификации может указываться, что истребитель должен достигать скорости в 2 числа Маха, иметь дальность полета-3000 километров, стоить не более SO миллионов долларов и т.д. Н о како й бы подробной ни была спецификация, это еще не проект самолета, и тем более не сам самолет. Авиастрои­ тельная компания берет такую спецификацию и создает на ее основе один или нес колько проектов. К омпании-конкуренты могут разработать несколько разных проектов, причем кажды й из них предположительно соответствует исходно й спецификации .

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

Хотя это и не очевидно, здесь работает то же самое понятие СПЕЦИФИКАЦИИ, что и в случае проверки пригодности или отбора по запросу. М ы задаем критерии для объектов, которых еще нет в наличии в данный момент. Но программная реализация будет совершенно другой. Такая СПЕЦИФИКАЦИЯ не является фильтром для уже существующих объектов, как в случае отбора по запросу. Это и не проверка одного СУlцествующего объекта на соответствие каким-то критериям. На этот раз в соответствии со СПЕЦИФИКАЦИЕЙ будет создаваться или переконфигурироваться совершенно новый объект или набор объектов .

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

Но вместо этого мы вводим для генератора тако й интерфейс, который определяется в терминах описательно й СПЕЦИФИКАЦИИ и задает явные ограничения для сво йств про­ дукции генератора. В этом подходе имеется ряд преимуществ .

Реализация генератора отделена от его интерфей са. СПЕЦИФИКАЦИЯ определяет • требования к результату, но не способ его достижения .

Интерфейс передает заданные регламентные правила в явно й форме, так что раз­ • работчики знаIОТ, чего ожидать от генератора, даже не понимая всех подробностей его работы. Между тем единственны й способ описать поведение процедурно­ определенного генератора - это проанализировать весь его код построчно или за­ пустить серию тестов .

Интерфе йс обладает большей гибкостыо или же ему можно придать большую • гибкость, поскольку постановка задачи возложена на клиентский код, а от генера­ тора требуется только выполнение требований, изложенных в СПЕЦИФИКАЦИИ .



Pages:     | 1 || 3 | 4 |   ...   | 5 |


Похожие работы:

«РУКОВОДСТВО ПО ЭКСПЛУАТАЦИИ Моноблоки Proton габарит 4 Сплит-системы Proton габариты 4/5 СОДЕРЖАНИЕ 1. Важные замечаний по безопасности 2 . Описание холодильной машины 2.1. Назначение холодильной машины 2.2. Технические характеристики 2.3. Устройство холодильных машин 3. Работа холодильной машины 4. Переме...»

«ПРИКЛАДНАЯ МЕХАНИКА И ТЕХНИЧЕСКАЯ ФИЗИКА. 2013. Т. 54, N1 143 УДК 539.387 ИСПОЛЬЗОВАНИЕ СИГМОИДАЛЬНОЙ ФУНКЦИИ ПРИ РЕШЕНИИ ЗАДАЧ НЕЛИНЕЙНОГО ЦИЛИНДРИЧЕСКОГО ИЗГИБА ПЛАСТИН ИЗ ФУНКЦИОНАЛЬНО-ГРАДИЕНТНОГО МАТЕРИАЛА А. Каси,, К. Бахти, Х. Хебали,, А. Тунси Университет г. Сиди-Бель-Аббес, Сиди-Бель-Аббес, Алжир Универс...»

«Экраны для радиаторов Производство Производство основано в 1996 г. Площадь 7000 кв.м; 80 сотрудников Бренд "Ark Master" зарегистрирован в 2008 году; запущен обновленный сайт http://new.arkmaster.ru/ Персонал: Специализированное образование Тренинги и дополнительное совершенствование Современное оборудование: Основное SCM, ORMA, WEINIG, Mi...»

«ПЕРЕДВИЖНОЙ ФИЛЬТРОВЕНТИЛЯЦИОННЫЙ АГРЕГАТ С СИГНАЛИЗАЦИЕЙ EMK-1600 EMK 1 6.00.00.00 ПС Производитель: АО СовПлим, Россия, 195279, Санкт-Петербург, шоссе Революции, д.102, к.2 Тел.: +7 (812) 33-500-33 e-mail: in...»

«Инструкция по эксплуатации Дизельная одно / двухступенчатая горелка EL 02B.10 RU EL 02B.16 EL 02B.16 1D Март 2008 (08/03) DOC126821 1/20 Обзор Важные указания Применение / Рабочая зона Идентификация Важные указан...»

«БЕСПРОВОДНЫЕ РЕШЕНИЯ ДЛЯ СОВМЕСТНОЙ РАБОТЫ WiCS-2100 WiPG-1000 WiPG-1600W SharePod Встречайте семейство продуктов wePresent Беспроводные решения wePresent для совместной работы отвечают потребностям любой учебной аудитории или комнаты переговоров, что позволяет легко проводить презентации, в том числе с использованием ин...»

«3.122.005 ПС/2015/01 Электротехнический завод "КВТ" Россия, г. Калуга www.kvt.su ПАСПОРТ муфты соединительные термоусаживаемые для 4-х и 5-и жильных кабелей с пластмассовой изоляцией с броней и без брони на напряжение до 1 кВ не поддерживающих горение марок 4ПСТнг-LS-1, 4ПСТ(б)нг-LS-1 и 5ПСТнг-LS-1, 5ПСТ(б)нг-LS-1 Все опера...»

«МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное бюджетное образовательное учреждение высшего образования "УФИМСКИЙ ГОСУДАРСТВЕННЫЙ АВИАЦИОННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ"...»

«ХАРИТОНОВА Наталия Анатольевна УПРАВЛЕНИЕ РАСХОДАМИ ПРОМЫШЛЕННОГО ПРЕДПРИЯТИЯ (на примере черной металлургии) Специальности: 08.00.05 "Экономика и управление народным хозяйством" (экономика, организация и управление предприятиями, отраслями и комплексами в промышленности) 08.00.12 "Бух...»

«Конкурсная документация на проведение открытого публичного конкурса на получение грантов Российского научного фонда по приоритетному направлению деятельности Российского научного фонда "Проведение фундаментальных научных исследований и поисковых научных исследований отдельными научными группами"1. Конкурс на получение грантов Р...»

«Ершов Николай Алексеевич РАЗРАБОТКА ТЕХНОЛОГИИ ИССЛЕДОВАНИЯ ТОНКОСЛОИСТЫХ ТЕРРИГЕННЫХ КОЛЛЕКТОРОВ МЕТОДАМИ ЭЛЕКТРИЧЕСКОГО КАРОТАЖА С ВЫСОКИМ ВЕРТИКАЛЬНЫМ РАЗРЕШЕНИЕМ (НА ПРИМЕРЕ АЧИМОВСКИХ ОТЛОЖЕНИЙ ЗАПАДНОЙ СИБИРИ) Специальность 25.00.16 Горнопромышленная и нефтегазопромысловая геология, геофизика, маркш...»

«ФЕПЕРАПЬНАЯ ПОРОЖНАЯ СЛУЖБА РОССИИ ООН 218.012-99 ОТРАСЛЕВЫЕ ПОРОЖНЫЕ НОРМЫ "ОБШИЕ ТЕХНИЧЕСКИЕ ТРЕБОВАНИЯ К ОГРАЖПАЮШИМ УСТРОЙСТВАМ НА МОСТОВЫХ СООРУЖЕНИЯХ, РАСПОЛОЖЕННЫХ НА МАГИСТРАЛЬНЫХ АВТОМОБИЛЬНЫХ ПОРОГАХ" Издание официальное г. Москва окп ФЕДЕРАЛЬНАЯ ДОРОЖНАЯ С.ЧУЖЬА РОССИИ...»

«ИССЛЕДОВАНИЕ ТЕРРАСОВИДНОГО РЕЛЬЕФА НА ПОВЕРХНОСТИ СТЕНОК ПОР В БИОСОВМЕСТИМОМ ПОРИСТОМ НИКЕЛИДЕ ТИТАНА, ПОЛУЧЕННОМ СПЕКАНИЕМ А А В, А А В С.Г. Аникеев, Н.В . Артюхова, А.С. Гарин XV " В АВ А А А " Научный руководите...»

«УДК 579.841.11+577.18 БИОТЕХНОЛОГИЧЕСКИЕ ПОДХОДЫ К СОЗДАНИЮ ПРОДУЦЕНТОВ АНТИБИОТИКОВ НА ОСНОВЕ РИЗОСФЕРНЫХ БАКТЕРИЙ РОДА PSEUDOMONAS И.Н. Феклистова, Н.П . Максимова Белорусский государственный университет, Минс...»

«Организаторы конференции • Московский авиационный институт (национальный исследовательский университет) • Министерство образования и науки РФ • Российская академия наук • Российский фонд фунд...»

«№ 113780277 ОТКРЫТОЕ АКЦИОНЕРНОЕ ОБЩЕСТВО "СТРОИТЕЛЬНОЕ УПРАВЛЕНИЕ № 308" ИНН 7825661413 ОГРН 1027809199901 ЯВЛЯЕТСЯ ДЕЙСТВИТЕЛЬНЫМ ЧЛЕНОМ НЕКОММЕРЧЕСКОГО ПАРТНЕРСТВА САМОРЕГУЛИРУЕМОЙ ОРГАНИ...»

«Каток на центральной с 22 декабря площади г. Орла г. Орёл, пл. Ленина Во второй половине декабря жители и гости города Орла снова смогут покататься на коньках в самом центре города. В этом зимнем сезоне он также станет доступным для всех желающих вход будет бесплатным. Для...»

«Устройство согласования последовательного интерфейса "УС-ПИ" Этикетка БФЮК.425622.004 ЭТ 1. Общие сведения об изделии Устройство согласования последовательного интерфейса "УС1 – разъём 1; 2 – разъём 2. 1 – разъём 1; 2 – разъём 2; ПИ" (в дальнейшем УС-ПИ) предназначено для конфигурирования Рис.2 Кабель ком...»

«А К А Д ЕМ И Я Н А У К СССР Серия "Наука и технический прогресс" Основана в 1977 г. Е. И. Янтовский ПОТОКИ ЭНЕРГИИ И ЭКСЕРГИИ Ответственный редактор член-корреспондент АН СССР А. А. МАКАРОВ. МОСКВА "НАУКА" ББК 31 Я60 УДК 620.9 Рецензенты: доктора технических наук Ю. И. БОКСЕРМАН, И. Л. ВАРШАВСКИЙ Литовский Е. И. Я601 4 4 с °Т Г...»

«Министерство образования и науки Российской Федерации Санкт-Петербургский государственный архитектурно-строительный университет КОМПОЗИТНЫЕ МАТЕРИАЛЫ В СТРОИТЕЛЬСТВЕ ОБЪЕКТОВ ТРАНСПОРТНОЙ ИНФРАСТРУКТУРЫ Материалы на...»

«РЕЕСТР ИНВЕСТИЦИОННЫХ ПЛОЩАДОК МУНИЦИПАЛЬНОГО ОБРАЗОВАНИЯ "МЕЖДУРЕЧЕНСКИЙ ГОРОДСКОЙ ОКРУГ" ОПИСАНИЕ АКТИВНЫХ ИНВЕСТИЦИОННЫХ ПЛОЩАДОК МЕЖДУРЕЧЕНСКОГО ГОРОДСКОГО ОКРУГА Площадка №1 под строительство...»

«Секция 3: Автоматизация, информатизация и менеджмент на предприятии СОВЕРШЕНСТВОВАНИЕ ТЕХНИЧЕСКОГО СЕРВИСА ОБОРУДОВАНИЯ ПРОИЗВОДСТВЕННЫХ ПРЕДПРИЯТИЙ Г.В. Редреев, к.т.н., доц., А.В. Шимохин, к.э.н., П.В. Кийко, к.п.н., доц. Омский государственный аграрный университет...»

«лист К 1 Приложение к свидетельству М об утверждении типа средств измерений всего листов 5 серийного производства ОПИСАНИЕ ТИПА СРЕДСТВ ИЗМЕРЕНИЙ Согласовано ководитель ГЦИ СИ Ое, р @ЕСТ РЕ 6 у4 ТЕ тель...»







 
2019 www.librus.dobrota.biz - «Бесплатная электронная библиотека - собрание публикаций»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.