М. Фаулер, К. Скотт - UML. Основы - 2002 (1158629), страница 31
Текст из файла (страница 31)
Это можно было бы осуществить, создав интерфейс «величина» и установив, что Количество реализует данный интерфейс, но это привело бы к определенным трудностям. В языке Бша11Са1к подобных проблем не возникает, да и в языке С++ для этой цели можно воспользоваться параметризованными типами. Что касается данного примера, то здесь целесообразно использовать класс КоличественныйДиапазон, который, в свою очередь, использует образец «Диапазон«.
Наблюдение пациента: модель предметной области 155 Рие. 11.3. Другая диаграмма обэентов наблюдения нациенога Проблема, связанная с Наблюдением, заключается в том, что Наблюдение одновременно может быть Категорией Наблюдения и Измерением (рис. 11.3). В языке дача, как и в большинстве других языков программирования, можно определить только одну классификацию. Я решил эту проблему, допустив, что любое Наблюдение должно иметь ассоциированный с ним Показатель, который позволяет классу Наблюдение аффективно реализовывать как понятие Наблюдение, так и понятие Категория Наблюдения.
Хотя эти решения далеки от совершенства, тем не менее, они позволяют выполнить намеченную работу, Не пытайтесь создать программное обеспечение, которое в точности отражало бы концептуальную точку зрения. Напротив, следует придерживаться не буквы, а духа концептуальной модели, учитывая при этом ограничения имеющихся средств реализации. 156 Глава 11. Язык 0М~ и программирование Наблюдение пациента: модель спецификации На рис. 11.4 отражены модификации, которые внесены в модель предметной области, чтобы учесть некоторые из факторов, связанных с языком реализации. Рис.
11.4. Модель наблюдения пациента уровня спецификации Модель наблюдения пациента представлена здесь с точки зрения спецификации. На ней указаны скорее интерфейсы классов, чем сами классы. Можно было бы поддерживать эту концептуальную модель и далее, но более вероятно, что начиная с етого момента, я буду работать только с моделью спецификации. Я не стараюсь поддерживать слишком много моделей. Мое личное правило состоит в следующем: если я не могу поддерживать модель посредством внесения в нее необходимых обновлений, то она отправляется в корзину.
(Я знаю, что я к тому же ленив!) Теперь рассмотрим поведение, ассоциированное с нашей моделью наблюдения пациента (рис. 11.5). Наблюдение пациента: модель спецификации 157 Рис. 11.Б. Операции модели наблюдения пациента Первый сценарий запрашивает последние данные о сердечном ритме пациента. В связи с этим возникает первый вопрос: кто должен нести ответственность за выполнение этого запроса2 Естественным ответом представляется Пациент. Пациент должен просмотреть все свои наблюдения, выбрать из них измерения с Типом Показателя «сердечный ритм» и найти среди них самое последнее значение.
Чтобы сделать это, необходимо добавить в Измерение момент времени. Поскольку аналогичные рассуждения могут быть применены и к другим наблюдениям, я также добавлю момент времени и в Наблюдение. Пациент имеет такую же ответственность по отношению к Показателю: Найти последнюю Категорию Наблюдения, которая обладает Показателем для данного Типа Показателя. На рис. 11.5 показаны те операции, которые были мною добавлены к классу Пациент, чтобы отразить в модели все сказанное выше. 158 Глава Ц. Язык ОМ1. и программирование Не пытайтесь слишком тщательно определять операции, если на данный момент они не являются совершенно очевидными. Самое важное сейчас — это установить ответственность. Если вы можете облечь ее в форму операции, то это прекрасно; в противном случае для описания ответственности вполне достаточно и короткой фразы.
Обновление измерения уровня сознания пациента включает создание нового Наблюдения соответствующего Показателя. Выполняя эти действия, пользователь обычно предпочитает выбрать Показатель из некоторого «всплывающего» списка. Это можно реализовать, связав объекты Показатель с конкретным Типом Показателя, поскольку данная ответственность подразумевается ассоциацией между ними. Добавляя некоторое измерение, тем самым мы должны создать новое Измерение. Некоторые дополнительные сложности вытекают из того факта, что это новое Измерение должно установить, существует ли Показатель, который может быть для него назначен.
В этом случае Измерение может запросить ассоциированный с ним Тип Показателя, имеется ли соответствующий Показатель. Таким образом, между данными объектами существует некоторая кооперация. Она может быть хорошо представлена с помощью диаграммы последовательности (рис. 11.6). Должны ли вы строить все эти диаграммы? Вовсе нет. Многое зависит от ваших способностей к визуализации моделей и от того, насколько легко вам работать с выбранным языком программирования.
Что касается языка Зта111а1)г, то зачастую писать код так же легко, как и разрабатывать диаграммы. Если же программа разрабатывается на языке С++, то диаграммы приносят больше пользы. Диаграммы не должны быть произведениями искусства. Я обычно изображаю их в виде схем на листе бумаги или небольшой доске. Я прибегаю к помощи графического редактора (или САЗЕ-средства) только тогда, когда считаю целесообразным тратить усилия на поддержание их в состоянии внесения возможных обновлений, поскольку они помогают внести ясность в поведение классов.
На этой стадии проекта я могу также пользоваться СВС-карточками (см. главу б) в дополнение или вместо тех диаграмм, которые описаны в данной главе. Переход к кодированию Теперь мы можем обратиться к рассмотрению фрагментов программного кода, реализующего те идеи, которые обсуждались в предыдущих разделах. Я начну с Типа Показателя (РЬепошепоп Туре) и Показателя (РЬепошепоп), поскольку они довольно тесно взаимосвязаны.
Переход к кодированию 159 Рис. 11.6.Диаграмма последовательности наблюдения пациента Первое, на что следует обратить внимание, — это ассоциация между ними: должен ли интерфейс допускать навигацию в обоих направлениях г По моему мнению, в данном случае именно так и должно быть, поскольку при этом оба направления будут одинаково важными и, в любом случае, эти понятия тесно связаны друг с другом. На практике данную ассоциацию удается реализовать с помощью указателей в обоих направлениях. Однако эту ассоциацию я сделаю неизменяемой, поскольку даже при обновлении данных соответствующие объекты не будут модифицироваться часто, а если и будут, мы всегда можем создать их заново.
Некоторые разработчики испытывают трудности при работе с двунаправленными связями. Я не думаю, что могут возникнуть какие-либо проблемы, если обеспечить такую реализацию, при которой один класс несет полную ответственность за поддержание данной связи в актуальном состоянии, пользуясь при необходимости специальным «дружественным» или вспомогательным методом. Давайте рассмотрим некоторые определения.
160 Глава 11. Язык О(ч(Е и программирование РЧЫ1с с1авв РпепоаепопТуре ехтвпбв Оова1пОЬ(есС ( роы1с Рьепоаепоптуре (8сг1пр паве)( ворог (паве); ); чо(б Гг1епбРпепоаепопАбб (Рпвпоавпоп пвиРПепоаепоп) ( 'ст ОГРАНИЧЕН: используется только Показателем рлепоаепа.аббЕ1еаепС (пеиРЬеповепоп); ); РЧЫ1с чозб веСРПеповепв (8Сг1пр(] павел) ( Гог (1пС 1 = 0; 1 < павел.1епОСЬ; 1++) пви Рпеповепоп (павел(1), СЫв); ); роЫСс ЕповегаС1оп рлеповепв() ( гесчгп рьепааепа.е1евепсв(); рг1чвте Честог рлепоаепв = пои ЧестогЦ; рг1чате Ооапт(туаапре ча11баапсв; ) Здесь используется соглашение, в соответствии с которым ко всем полям перед их именем добавляется символ подчеркивания. Это помогает избежать недоразумений, связанных с именами.
рсЫ1с с1ввв Раеповепоп ехтепбв Оовв1пОЬ)всС ( роЫ1с Рлепоаепоп(зтг1пр паве, РЬеповепопТуре Суре) ( варяг (паве); Суре = Суре; Суре.тг1епбРпеповепопАбб(СЫв); ): роЫ1с РаепоаепопТуре рлвпоаепопТурв() ( гетогп Суре; рг1чатв РпвповепопТуре Суре; рг1чатв ОовпС11уаапре гвпре; рвсхвре оЬвегчвС1опв; роЫ1с с1ввв Оова1пОЬ)есС ( роЫ1с Ооаа1пОЬ)ест(8Сг1пр паве) ( паве = паве; роЫ1с Ооав1пОЬ)есС() (); 161 Переход к кодированию роЫ1с Бтг1пц папе() ( ге1с гп папе; роЫ1с 81г1пц 108(г1пц() ( гете гп паве; ): рго1ес1еб 81г1пц паап = "по паев"; Я добавил класс с именем [)оп)а)пОо)ес1 (Объект Предметной Области), который располагает информацией об именах и сможет реализовать любое другое поведение, которое потребуется от всех моих классов предметной области.
Теперь я могу описать эти объекты с помощью следующего кода: РПепоеепопТуре зех = псе РпепоеепопТуре("цепбег").Регз1з1(); Бгг1пц[) зехез = (-еа1е", "Теаа)е"); зех.зеСРПепоаепа(зехез); Операция регв[81() сохраняет Тип Показателя в специальном объекте- реестре, чтобы впоследствии его снова можно было получить с помощью статического метода не1(). Детали реализации этого я опущу. Теперь рассмотрим фрагмент программного кода, который реализует ввод новых наблюдений пациента.