Сервер Банковских Транзакций, Менеджер

Транзакций Проверки ПИН-кода,

Менеджер Транзакций Снятия, Менеджер

Транзакций Справки, Менеджер

Транзакций Перевода, Чековый Счет,

Сберегательный Счет, Дебетовая Карточка,

Карточный Счет, Протокол Транзакций;

временные характеристики:

активизация: асинхронная – по мере поступления сообщений от клиен­тов. Интервал между последовательными поступлениями в худшем слу­чае 100 мс. Среднее время между поступлениями больше 1 с;

время выполнения Сi: 10 мс на одно сообщение;

приоритет.

Высокий – должна успевать отвечать на входные сообщения;

обнаруживаемые ошибки.

Неопознанное сообщение;

логика упорядочения событий.

Определяется на этапе детального проектирования программы.

9. Проектирование классов

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

9.1. Проектирование классов, скрывающих информацию

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

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

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

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

классы интерфейса устройства, взаимодействующие с устройствами вво­да/вывода;

классы интерфейса пользователя, осуществляющие человеко-машинный интерфейс;

классы интерфейса системы, общающиеся с внешней системой или под­системой;

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

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

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

9.2. Проектирование операций классов

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

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

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

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

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

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

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

Рассмотрим, к примеру, класс Карточка Банкомата, который инкапсулирует информацию, прочи­танную с карточки. Из диаграммы кооперации в составе аналитической модели (рис.9.la) видно, что объект Интерфейс Устройства Считывания Карточек посылает сообщение Данные от Устройства Считывания сущностному объек­ту Карточка Банкомата. Позже объект Интерфейс Клиента отправляет сооб­щение Запрос Карточки объекту Карточка Банкомата, который возвращает Данные Карточки. На этапе проектирования определяется точный интерфейс класса. Простое сообщение Данные от Устройства Считывания из проектной модели (рис.9.1б) отображается на вызов операции писать(данныеКарточки, предоставляемой объектом Карточка Банкомата. Сообщение Запрос Кар­точки отображается на вызов операции читать объекта Карточка. Простому сообщению Данные Карточки соответствует выходной параметр операции чи­тать. Вызовы операций изображаются с помощью нотации UML для синхрон­ных сообщений. На рис.9.1в показан класс абстрагирования данных Карточка Банкомата. Представлены как атрибуты, так и операции. У операции писать имеется один входной параметр – данные Карточки. У операции читать есть один выходной параметр – прочитанные данныеКарточки.

Рис.9.1. Пример класса абстрагирования данных:

а – аналитическая модель (диаграмма кооперации); б – проектная модель (диаграмма кооперации); в – проектная модель (диаграмма классов)

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

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

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

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

9.3. Классы абстрагирования данных

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

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

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

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

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

Рис.9.2. Пример класса абстрагирования данных:

а – аналитическая модель (диаграмма кооперации); б – проектная модель (диаграмма кооперации); в – проектная модель (диаграмма классов)

Теперь мы можем специфицировать операции класса Наличные Банкомата так, как показано на диаграмме кооперации проектной модели на рис.9.26. Нужны две операции: добавитьНаличные и выдатьНаличные. У операции выдатьНаличные один входной параметр: суммаКВыдаче – и три выходных: выдатьПяти-долларовыхКупюр, выдатьДесятидолларовыхКупюр, вы-датьДвадцатидолларовыхКупюр. У операции добавитьНа-личные три входных параметра: добавленоПятидолларовых Купюр, добавленоДесятидолларовыхКупюр и добавлено ДвадцатидолларовыхКупюр. Вот как специфицируются операции:

выдатьНаличные(in суммаКВыдаче,

out выдать ПятидолларовыхКупюр,

out выдатьДесятидолларовыхКупюр,

out выдатьДвадцатидолларовыхКупюр)

добавитьНаличные(in добавлено

ПятидолларовыхКупюр,

in добавленоДесятидолларовыхКупюр,

in добавленоДвадцатидолларовыхКупюр)

Диаграмма классов изображена на рис.9.2в.

Объекты этого класса поддерживают инвариант: общая сумма наличных в банкомате должна равняться суммарному значению достоинств всех купюр:

имеющаясяСумма = 5 * пятидолларовыеКупюры + . + 10 * десятидолларовыеКупюры + 20 * двадцати-долларовыеКупюры

Недостаток наличных – это ошибка, которую нужно распознавать. Обычно такие ошибки обрабатываются как исключения.

9.4. Классы интерфейса устройства

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

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

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

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

9.4.1. Проектирование операций классов интерфейса устройств. Экземпляры класса интерфейса устройства взаимодействуют с реальным устройством и предоставляют операции для считывания и записи.

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

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

В качестве примера рассмотрим объект Интерфейс Бензобака, у которого есть операции инициализировать и читать. Аналитическая модель (рис.9.3а) показывает, что этот объект получает запрос Читать Количество Топлива от объекта Расход Топлива. Затем объект Интерфейс Бензобака просит объект Бензобак сообщить, сколько бензина осталось. В проектной модели (рис.9.36) вызывается операция читать внешнего объекта Бензобак, которая возвращает ответ данныеБензобака. Как это происходит на самом деле, зависит от реализа­ции физического интерфейса. Так или иначе, объект Интерфейс Бензобака возвращает вызываю­щему объекту количествоТоплива.

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

Класс Интерфейс Бензобака также изображен на диаграмме класса (рис.9.3в). У него две операции. Операция инициализировать вызывается в начальный мо­мент. Для операции читать указан выходной параметр количество Топлива. Та­ким образом, сигнатура операции на диаграмме класса идентична тому, что мы видим на диаграмме кооперации.

9.4.3. Классы интерфейса устройства вывода. Класс интерфейса устройства вывода также должен иметь операцию иници­ализировать. Кроме того, в нем должна быть операция вывода. Например, в классе интерфейса устройства Дроссель есть операция вывести. Но если устрой­ство вывода может выполнять различные действия, то состав операций класса необходимо адаптировать. Так, класс Устройство Выдачи Наличных имеет опе­рацию выдатьНаличные, класс Устройство Печати Чеков – операцию напечататьЧек, класс Дверь Лифта – операции открыть и закрыть, а класс Мотор Лифта – операции вверх, вниз и стоп.

Рис.9.3. Пример класса интерфейса устройства ввода:

а – аналитическая модель (диаграмма кооперации); б – проектная модель (диаграмма кооперации); в – проектная модель (диаграмма классов)

Рассмотрим диаграмму кооперации из аналитической модели на рис.9.4а. Объект Интерфейс Маршрутного Дисплея, помеченный стереотипом «интерфейс устройства вывода», получает сообщения Средняя Скорость и Средний Рас­ход и выводит их на Маршрутный Дисплей.

Рис.9.4. Пример класса интерфейса устройства вывода:

а – аналитическая модель (диаграмма кооперации)

В классе Интерфейс Маршрутного Дисплея (рис.9.4в) есть две операции: вывестиСреднююСкорость (скорость) и вывестиСреднийРасход (расход Топлива). В данном случае объект-алгоритм Средняя Скорость вызывает опера­цию вывестиСреднююСкорость, передавая текущее значение средней скорости. На диаграмме кооперации в проектной модели это изображено в виде синхронного сообщения без возвращаемого значения (рис.9.46). Соответственно объект-алго­ритм Расход Топлива вызывает операцию вывестиСреднийРасход, передавая текущее значение расхода топлива. Класс Интерфейс Маршрутного Дисплея скрывает информацию о том, как форматируются данные и как осуществляется взаимодействие с физическим Маршрутным Дисплеем.

Рис.9.4. Пример класса интерфейса устройства вывода:

б – проектная модель (диаграмма кооперации);

в – проектная модель (диаграмма классов)

9.5. Классы, зависящие от состояния

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

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

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

обработатьСобытие (событие)

текущееСостояние ( ) : Состояние

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

Другой, более гибкий подход к проектированию операции обработатьСо­бытие состоит в том, чтобы не выполнять действие самостоятельно, а вернуть его в выходном параметре. Тогда сигнатура принимает вид:

обработатьСобытие (in событие, out действие)

Такой зависящий от состояния класс оказывается достаточно общим для ин­капсуляции любой таблицы переходов. Устройство таблицы зависит от приложе­ния и определяется в момент создания или инициализации объекта класса. Пример зависящего от состояния класса в системе круиз-контроля – это класс Управление Калибровкой. На диаграмме кооперации из аналитической модели (рис.9.5а) показан объект Управление Калибровкой, исполняющий диаграм­му состояния на рис.9.56.

Рис.9.5. Пример зависящего от состояния управляющего класса:

а – аналитическая модель (диаграмма кооперации); б – аналитическая модель (диаграмма состояний); в – проектная модель (диаграмма кооперации); г – проектная модель (диаграмма классов)

Объект Управление Калибровкой получает события Начать Калибровку и Прекратить Калибровку от двух объектов Интерфейс Кнопки Калибровки. Если получено сообщение Начать Калибровку (событие Ca1.1), то диаграмма состояний (рис.9.56) показывает, что должно быть выполнено действие Начать (Са1.2). В результате объект Управление Калибровкой посылает сообщение Начать объекту Калибровочная Константа, который выполняет действие. Если же получено сообщение Прекратить Калибровку (событие Са2.1), то из диа­граммы состояний следует, что нужно выполнить действие Прекратить (Са2.2). Теперь объект Управление Калибровкой отправляет сообщение Прекратить объекту Калибровочная Константа.

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

9.6. Классы, скрывающие алгоритмы

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

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

.

Рис.9.6. Пример класса-алгоритма: а – аналитическая модель (диаграмма кооперации); б – проектная модель (диаграмма кооперации); в –проектная модель (диаграмма классов)

9.7. Классы интерфейса пользователя

Класс интерфейса пользователя скрывает от других классов детали человеко-машинного интерфейса. В зависимости от приложения интерфейс пользователя может быть очень простым (например, в виде командной строки) или весьма сложным (графический интерфейс пользователя – ГИП). Чтобы реализовать ин­терфейс командной строки, достаточно одного класса, а для графического интер­фейса требуется, как правило, несколько. Низкоуровневые классы интерфейса пользователя – это элементы управления, находящиеся в библиотеках компонен­тов: окна, меню, кнопки и диалоги. Высокоуровневые классы пользовательского интерфейса часто составляются из таких классов.

В аналитической модели акцент делается на идентификации составных клас­сов пользовательского интерфейса и сборе информации о том, какие данные пользователь должен вводить, а какие ему надо показать. На этой же стадии мож­но проектировать выводимые формы. В проектной модели приложения с графи­ческим интерфейсом нужно определить классы для каждой экранной формы В качестве примера рассмотрим класс Интерфейс Клиента (рис.9.7а). Это основной интерфейс клиента банкомата. В классе Интерфейс Клиента есть опе­рации для каждого окна, используемого для диалога с пользователем: вывести ОкноПИН-кода, вывестиОкноСнятияДенёг, вывестиОкноПеревода Денег и вывестиОкноСправки – а также для вывода меню: вывестиМеню. Имеется опе­рация для работы с маленьким окном, предназначенным для вывода приглашений и сообщений пользователю, но не для ввода данных. Параметром такой операции служит идентификатор выводимого сообщения. Каждое окно показывает некото­рую информацию, а затем, возможно, ожидает ввода данных, которые возвраща­ются в виде выходного параметра.

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

Рис.9.7. Пример класса интерфейса пользователя:

а – класс интерфейса пользователя с операциями;

б – структура составного класса пользовательского интерфейса

9.8. Классы бизнес-логики

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

Примером такого класса может служить класс Менеджер Транзакций Снятия (рис.9.8), где инкапсулированы правила обработки запроса на снятие денег.

Рис.9.8. Пример класса бизнес-логики: а – аналитическая модель (диаграмма кооперации); б – проектная модель (диаграмма кооперации); в – проектная модель (диаграмма классов)

В нем есть операции инициализировать, снять, подтвердить, и отменить. Операция инициализировать вызывается на этапе инициализации, операция снять – для снятия денег со счета клиента, операция подтвердить – для под­тверждения факта успешного проведения транзакции, а операция отменить – в случае, когда транзакция закончилась неудачно, в частности если банкомат не выдал наличные. При определении операций необходимо тщательно проанализи­ровать диаграмму кооперации Банковский Сервер из аналитической модели (рис.9.8а) и описание последовательности сообщений, где приведены детальные сведения о каждом сообщении. По результатам такого анализа стро­ится диаграмма кооперации для проектной модели (рис.9.86) и диаграмма клас­сов (рис.9.8в).

9.9. Классы-обертки базы данных

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

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

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

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

Рис.9.9. Пример класса-обертки базы данных:

а – аналитическая модель; б – проектная модель

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

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

Необходимо спроектировать класс-обертку Дебетовая Карточка (рис.9.96), в котором будут следующие операции: создать, проверить, проверитьСуточныйЛимит, обнулитьИтог, обновить, читать и удалить. Эти операции инкапсулируют операторы SQL для доступа к отношению Дебетовая Карточ­ка. Обратите внимание, что атрибуты класса разрешается обновлять независимо, поэтому для каждого атрибута имеется своя операция обновить. Вызов любой операции приводит к выполнению некоторого оператора SQL.

9.10. Внутренние программные классы

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

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

9.11. Применение наследования при проектировании

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

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

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

9.11.2. Абстрактные классы. У абстрактного класса не может быть экземпляров, поэтому он используется как шаблон для создания классов, а не объектов. Иными словами, он способен выступать только в роли суперкласса, который определяет общий интерфейс для всех своих подклассов. Абстрактная операция – это операция, которая объявле­на, но не реализована в абстрактном классе. Абстрактный класс должен иметь хотя бы одну абстрактную операцию.

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

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

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

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

9.12. Примеры наследования

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

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

§ открыть (номерСчета : Integer)

§ закрыть ()

§ читатьБаланс () : Real

§ кредитовать (сумма : Real)

§ дебетовать (сумма : Real)

Банк работает с двумя видами счетов: чековыми и сберегательными. Ситуа­ция выглядит вполне подходящей для применения наследования: нужно создать обобщенный суперкласс счета и его специализированные подклассы для чековых и сберегательных счетов. На данном этапе следует ответить на такие вопросы: ка­кие обобщенные атрибуты и операции должны быть в суперклассе и какие специ­ализированные атрибуты – у сберегательного и чекового счета? Должен ли су­перкласс быть абстрактным?

Но прежде чем отвечать, надо выявить сходство и различие между чековыми и сберегательными счетами. Сначала рассмотрим атрибуты. Ясно, что у любого счета есть номер и баланс, следовательно, эти атрибуты можно обобщить и по­местить в класс Счет. С другой стороны, в данном банке на сберегательные счета начисляются проценты, а на чековые - нет. Нужно знать сумму начисленных про­центов, поэтому в классе Сберегательный Счет объявляется атрибут накопительныйПроцент. Кроме того, со сберегательного счета разрешается безвозмезд­но снимать деньги не более трех раз в месяц, что приводит к объявлению атрибута счетчикДебетований. Еще объявляются два статических атрибута класса, их значение одинаково для всех объектов данного класса: максимальноеЧислоБесплатныхДебетований (сколько раз в месяц можно снимать деньги со счета, не оплачивая банковские услуги; начальное значение равно 3) и платаБанку (сум­ма, выделяемая банку за каждое снятие денег сверх лимита; равна 1,5 доллара). Кроме того, в случае чековых счетов желательно знать, какая сумма была положе­на в последний раз. Поэтому в классе Чековый Счет объявляется атрибут суммаПоследнегоВклада.

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

Рис.9.10. Пример суперкласса и подклассов

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

Проект иерархии класса Счет показан на рис.9.10 и прокомментирован ниже.

Проект подкласса «Чековый Счет»

1. Атрибуты подкласса Чековый Счет:

– наследует атрибуты номерСчета и баланс. Оба атрибута объявлены за­щищенными в суперклассе Счет, поэтому они видимы в подклассах;

– добавляет атрибут суммаПоследнегоВклада.

2. Операции подкласса Чековый Счет:

– наследует операции открыть, закрыть, читатьБаланс, кредитовать и дебетовать;

– добавляет операцию читатьСуммуПоследнегоВклада () : Real

Проект подкласса «Сберегательный Счет»

1. Атрибуты подкласса Сберегательный Счет:

– наследует атрибуты номерСчета и баланс;

– добавляет атрибуты накопительныйПроцент и счетчик Дебетований;

– добавляет статические атрибуты класса максимальное ЧислоБесплатныхДебетований и платаБанку. Статические атрибуты в UML подчер­киваются (см. рис.9.10).

2. Операции подкласса Сберегательный Счет:

– наследует спецификацию и реализацию операций открыть, закрыть, читатьБаланс и кредитовать;

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

– добавляет операции:

– начислитьПроцент (процентнаяСтавка : Real). Ежедневное на­числение процентов;

– читатьНакопительныйПроцент () : Real;

– обнулитьСчетчикДебетований (). Сбрасывает счетчикДебетований в нуль в начале каждого месяца.

9.12.2. Пример полиморфизма и динамического связывания. Рассмотрим теперь создание объектов этих классов, а также пример полимор­физма и динамического связывания.

begin

private account : Счет;

Предложить клиенту ввести тип счета и снимаемую сумму

if клиент выбрал чековый счет

then -- Записать в переменную account чековый счет клиента.

. . .

account := customerCheckingAccount;

. . .

elseif клиент выбрал сберегательный счет

then -- Записать в переменную account сберегательный счет клиента.

. . .

account := customerSavingsAccount;

endif;

. . .

-- Дебетовать счет account, который может быть чековым или сберегательным.

account. дебетовать (сумма);

. . .

end;

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

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

9.12.3. Пример наследования абстрактному классу. Рассмотрим класс Техническое Обслуживание из системы круиз-контроля и мониторинга. Классы Замена Масла, Замена Воздушного Фильтра и Стан­ционное ТО можно проектировать отдельно, но, если сделать их подклассами класса Техническое Обслуживание, кода придется писать гораздо меньше. Иерархия обобщения/специализации для класса Техническое Обслуживание показана на рис.9.11.

Рис.9.11. Пример абстрактного суперкласса и подклассов

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

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

9.13. Спецификация интерфейса класса

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

– описание информации, скрываемой классом, например инкапсулированную структуру данных (если речь идет о классе абстрагирования данных) или интерфейс устройства;

– критерий, на основании которого был выделен данный класс;

– предположения, сделанные при специфицировании класса: могут разные за­дачи получать одновременный доступ к операциям объекта этого класса или доступ должен быть строго последовательным;

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

– суперкласс (если есть);

– унаследованные операции (если есть);

– операции класса. Для каждой операции необходимо определить:

выполняемую функцию;

предусловие (условие, которое должно выполняться перед вызовом опе­рации);

постусловие (условие, которое должно выполняться после вызова опе­рации);

инвариант (условие, которое должно выполняться всегда);

входные параметры;

выходные параметры;

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

Спецификацию интерфейса класса можно представить в описательной или табличной форме. Ниже приводится пример описательной документации.

9.13.1. Пример спецификации интерфейса класса. Для иллюстрации возьмем класс, абстрагирующий данные о датчиках и при­водах (рис.9.12).

Рис.9.12. Пример класса, для которого составляется спецификация интерфейса

Скрывающий информацию класс. Хранилище Показаний Датчиков и При­водов.

Скрываемая информация. Инкапсулирует структуры данных, связанных с датчи­ками и приводами. Хранит текущие показатели датчиков и приводов.

Критерий выделения класса. Класс абстрагирования данных.

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

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

Суперкласс. Нет.

Унаследованные операции. Нет.

Предоставляемые операции:

1. читатьДатчик (in идДатчика, out значениеДатчика)

Функция. Возвращает текущее значение указанного датчика.

Предусловие. Значение датчика ранее было обновлено.

Инвариант. Значение датчика не изменяется.

Постусловие. Прочитано значение датчика.

Входные параметры. идДатчика.

Выходные параметры. значениеДатчика.

Вызываемые операции. Нет.

2. обновитьПривод (in идПривода, in значениеПривода)

Функция. Обновляет значение указанного привода перед выводом.

Предусловие. Привод существует.

Постусловие. Значение привода обновлено.

Входные параметры. идПривода, значениеПривода.

Выходные параметры. Нет.

Вызываемые операции. Нет.

3. обновитьДатчик(in идДатчика, in значение Датчика)

Функция. Заменяет значение указанного датчика новым показанием, про­читанным с внешнего устройства.

Предусловие. Датчик существует.

Постусловие. Значение датчика обновлено.