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

advertisement
Сервер Банковских Транзакций, Менеджер
Транзакций Проверки ПИН-кода,
Менеджер Транзакций Снятия, Менеджер
Транзакций Справки, Менеджер
Транзакций Перевода, Чековый Счет,
Сберегательный Счет, Дебетовая Карточка,
Карточный Счет, Протокол Транзакций;
• временные характеристики:
– активизация: асинхронная – по мере поступления сообщений
от клиентов. Интервал между последовательными поступлениями в
худшем случае 100 мс. Среднее время между поступлениями больше
1 с;
– время выполнения Сi: 10 мс на одно сообщение;
• приоритет.
Высокий – должна успевать отвечать на входные сообщения;
• обнаруживаемые ошибки.
Неопознанное сообщение;
• логика упорядочения событий.
Определяется на этапе детального проектирования программы.
9. Проектирование классов
На этом этапе проектируются скрывающие информацию классы,
из которых создаются экземпляры пассивных объектов.
Первоначально классы определяются на этапе построения
аналитической модели в ходе разбиения системы на объекты и
классы. Класс применяет сокрытие информации для решения
различных вопросов, связанных со статической структурой, например
сокрытия информации об устройстве или абстрагирования данных.
Операции класса можно вывести как из статической, так и из
динамической модели. Хотя статическая модель предназначена для
отражения операций классов, их проще выявить посредством анализа
динамической модели – прежде всего, диаграммы кооперации или
последовательности. Ниже мы покажем, как операции класса
выводятся из модели кооперации, из описания конечного автомата и
из статической модели, но основное внимание уделим модели
кооперации.
191
9.1. Проектирование классов, скрывающих информацию
Скрывающие информацию классы, проектируемые на этом
этапе, подразделяются на категории в соответствии со стереотипом.
Они документируются с помощью спецификаций интерфейсов.
Классы, выводимые из аналитической модели, то есть те,
которые
определены
спецификой
предметной
области,
подразделяются на сущностные, интерфейсные, управляющие и
относящиеся к логике приложения. Кроме них существуют классы,
зависящие от области решения; они проектируются позже по мере
необходимости:
– сущностные классы, представленные в аналитической модели,
служат для инкапсуляции данных. На диаграммах классов они
помечаются стереотипом «сущность». Сущностные объекты –
экземпляры сущностных классов - обычно хранят в себе информацию
и существуют в течение длительного времени. Для приложений,
работающих с базами данных, часто требуется хранить
инкапсулированные данные в базе. В таком случае сущностный
класс скорее предоставляет интерфейс к базе, чем инкапсулирует
данные. Поэтому на этапе проектирования классов сущностные
классы подразделяются на классы абстрагирования данных,
инкапсулирующие структуры данных, и на классы-обертки. Классобертка скрывает детали взаимодействия с внешней системой, в
которой хранятся данные. В качестве примера можно привести
файловую систему или систему управления базами данных. Классобертка базы данных скрывает информацию о том, как данные хранятся в базе, обычно реляционной. Класс-обертка может также
скрывать информацию об интерфейсе с унаследованной системой;
– интерфейсные классы, реализующие интерфейс с внешней
средой, можно разделить на следующие группы:
классы интерфейса устройства, взаимодействующие с
устройствами ввода/вывода;
классы интерфейса пользователя, осуществляющие человекомашинный интерфейс;
классы интерфейса системы, общающиеся с внешней системой
или подсистемой;
– управляющие классы, координирующие совместную работу
нескольких объектов, которые участвуют в прецеденте. Среди них
можно выделить классы координации, классы, зависящие от
192
состояния, и классы таймера. Предполагается, что классы
координации и классы таймера активны (то есть являются задачами),
поэтому мы их обсуждать не будем;
– классы прикладной логики, инкапсулирующие особенности
логики и алгоритмов приложения, подразделяются на классы бизнеслогики и классы алгоритмов;
– внутренние программные классы, скрывающие те решения
проектировщиков, которые могут впоследствии измениться. Их
нельзя определить на основе аналитической модели, они появляются
в процессе проектирования.
9.2. Проектирование операций классов
Опишем, как выявляются операции, предоставляемые классами.
Хотя для отражения операций классов предназначена статическая
модель, их проще выявить на основе анализа динамической модели,
прежде всего из диаграммы кооперации. Это обусловлено тем, что в
динамической модели отражен обмен сообщениями между
объектами, а следовательно, и операции объекта-получателя.
Передача сообщений между пассивными объектами – не что иное,
как вызов операции одного объекта из операции другого объекта.
Представим несколько примеров проектирования классов. На
диаграммах кооперации показано, как пассивный объект, операции
которого мы проектируем, взаимодействует с другими объектами.
Эти объекты, в свою очередь, могут быть пассивными или
активными. Из приведенных примеров станет ясно, как диаграммы
кооперации из аналитической модели отображаются на диаграммы
кооперации проектной модели. Основное внимание в примерах
обращается на проектирование конкретного класса и его операций.
Прочие решения – например, об отображении объектов, вызывающих
необходимые операции, на задачи – мы сознательно не
рассматриваем.
9.2.7. Проектирование операций классов на основе модели
взаимодействия. Воспользуемся моделью взаимодействия объектов
для выявления операций классов. Для этой цели годится как
диаграмма кооперации, так и диаграмма последовательности.
Операции классов определяются путем рассмотрения того, как объект
данного класса взаимодействует с другими объектами, поскольку
взаимодействие двух объектов состоит в том, что один из них
193
вызывает операцию другого. При проектировании операций классов
мы будем применять в основном диаграммы кооперации.
Рассматривая два взаимодействующих объекта, необходимо
понять, чьи операции вызываются. Обычно для этого недостаточно
диаграммы классов из статической модели, так как на ней показаны
только статические отношения между классами. С другой стороны, из
динамической модели видно направление передачи сообщений от
одного объекта к другому. Если спроецировать объекты на последовательную программу, то окажется, что объект-отправитель
вызывает операцию объекта-получателя. Следовательно, сообщению
ставится в соответствие вызов операции. Имя сообщения
отображается на имя операции, а параметры сообщения – на
параметры операции.
Операции объекта допустимо определить непосредственно из
диаграммы взаимодействия, на которой этот объект присутствует. В
аналитической модели акцент ставится на той информации, которой
обмениваются объекты, а не на точном синтаксисе операции.
Поэтому имя сообщения на диаграмме кооперации может быть
существительным (отражающим тип передаваемых данных) или
глаголом (описывающим саму выполняемую операцию).
В модели проектирования специфицируются операции классов.
Если имя сообщения было существительным, необходимо определить
операцию объекта, который получит поименованную информацию.
Если же имя сообщения было глаголом, оно и становится именем
операции. Имя, данное операции в проектной модели, должно
отражать ее назначение в классе.
Важно также выяснить, у каких операций есть входные и
выходные параметры. В аналитической модели все сообщения на
диаграмме кооперации изображаются в виде простых сообщений,
посылаемых одним объектом другому. В некоторых случаях простое
сообщение – это ответ на полученное сообщение. На диаграммах
кооперации из проектной модели сообщения, вызывающие операции,
изображаются
как
синхронные.
Те
простые
сообщения,
представленные в аналитической модели, которые соответствуют
данным, возвращенным операцией, то есть ответам, отображаются на
выходные параметры операции.
Рассмотрим, к примеру, класс Карточка Банкомата,
который инкапсулирует информацию, прочитанную с карточки. Из
194
диаграммы кооперации в составе аналитической модели (рис.9.la)
видно, что объект Интерфейс Устройства Считывания
Карточек посылает сообщение Данные от Устройства
Считывания сущностному объекту Карточка Банкомата. Позже
объект Интерфейс Клиента отправляет сообщение Запрос
Карточки объекту Карточка Банкомата, который возвращает
Данные Карточки. На этапе проектирования определяется точный
интерфейс класса. Простое сообщение Данные от Устройства
Считывания из проектной модели (рис.9.1б) отображается на вызов
операции писать(данныеКарточки, предоставляемой объектом
Карточка
Банкомата. Сообщение Запрос
Карточки
отображается на вызов операции читать объекта Карточка.
Простому сообщению Данные Карточки соответствует выходной
параметр операции читать. Вызовы операций изображаются с
помощью нотации UML для синхронных сообщений. На рис.9.1в
показан класс абстрагирования данных Карточка Банкомата.
Представлены как атрибуты, так и операции. У операции писать
имеется один входной параметр – данные Карточки. У операции
читать есть один выходной параметр – прочитанные
данныеКарточки.
Рис.9.1. Пример класса абстрагирования данных:
а – аналитическая модель (диаграмма кооперации); б – проектная модель
(диаграмма кооперации); в – проектная модель (диаграмма классов)
195
После определения операции на основе диаграммы кооперации
она изображается на статической модели вместе с классом, который
ее содержит. Очень удобно одновременно выявлять операции из
диаграмм кооперации и изображать их на диаграммах классов.
9.2.2. Проектирование операций классов на основе конечного
автомата. Операции классов можно также вывести из диаграммы
состояний. На ней представлены действия и деятельности,
инициируемые в результате перехода состояний. Действия, как
правило, отображаются на операции класса. На диаграммах
состояний показаны все зависящие от состояния действия и
деятельности. Если действия выполняются пассивными классами, то
они выводятся из диаграммы состояний.
Действия и деятельности должны быть изображены также на
диаграмме кооперации или последовательности. Поскольку на этих
диаграммах
обычно
показываются
сценарии
прохождения
прецедентов (конкретные последовательности взаимодействий между
объектами), то редко встречающиеся альтернативы (к примеру,
обработка исключений) часто пропускаются. Таким образом, хотя
любое действие должно быть отражено на консолидированных
диаграммах кооперации, на какой-то отдельной диаграмме,
относящейся к конкретному прецеденту, его может и не быть. На
диаграмме состояний должны присутствовать все зависящие от
состояния действия и деятельности.
9.2.3. Проектирование операций классов на основе статической
модели. Проектировать операции классов на основе диаграмм
классов из статической модели тоже можно, особенно для
сущностных классов. Стандартными являются операции создать,
читать, обновить, удалить. Но зачастую состав операций легко
адаптировать под нужды конкретного класса абстрагирования
данных, определив, какие сервисы он должен предоставлять. Ниже
это будет проиллюстрировано на примерах. Здесь объекты,
вызывающие операции пассивных объектов, в основном тоже
считаются пассивными, хотя могут быть и активными.
9.3. Классы абстрагирования данных
Каждый сущностный класс из аналитической модели, который
инкапсулирует данные, проектируется как класс абстрагирования
данных. Сущностный класс хранит данные и предоставляет операции
196
для доступа к ним – операции чтения и записи. Класс
абстрагирования данных используется для инкапсуляции структуры
данных, то есть сокрытия деталей ее внутреннего представления.
Операции проектируются как процедуры иди функции доступа,
внутреннее устройство которых также скрыто.
Информация об атрибутах класса абстрагирования данных
должна уже присутствовать в статической модели предметной
области. Операции такого класса определяются путем рассмотрения
тех сервисов, которые нужны клиентским объектам для
опосредованного доступа к данным. Это можно сделать,
проанализировав способы использования объекта абстрагирования
данных в модели кооперации.
9.3.1. Пример класса абстрагирования данных. Для знакомства с
классами
абстрагирования
данных
рассмотрим
диаграмму
кооперации из аналитической модели (рис.9.2а), состоящую из двух
объектов, которым нужен доступ к объекту Наличные Банкомата.
Атрибуты данного объекта приведены в статической модели: это
количество пяти-, десяти- и двадцатидолларовых купюр в устройстве
выдачи наличных. Таким образом, в объекте должны быть
внутренние переменные, содержащие соответствующие счетчики
купюр. Таким образом, мы проектируем класс Наличные
Банкомата с четырьмя атрибутами: имеющаясяСумма,
пятидолларовыеКупюры,
десятидол-ларовые
Купюры,
двадцатидолларовыеКупюры.
Начальное
значение
всех
атрибутов установлено в ноль.
Необходимы сведения о том, какие сообщения посылаются
объекту Наличные Банкомата и какова последовательность их
отправки. Так, из аналитической модели явствует, что, когда объект
Наличные Банкомата получает сообщение Снимаемая со
Счета Сумма от объекта Интерфейс Устройства Выдачи
Наличных, он должен вычислить, сколько купюр каждого
достоинства выдать. В аналитической модели эта информация
посылается в ответном сообщении Ответ о Выдаче Наличных.
Объект Наличные Банкомата получает также сообщение от
объекта Интерфейс Оператора. Человек-оператор пополняет
запас наличных в банкомате купюрами разного достоинства, а
соответствующая информация передается объекту Наличные
197
Банкомата. Добавив наличные, оператор уведомляет банкомат с
помощью объекта Интерфейс Оператора, который посылает
сообщение Добавлены
Наличные
объекту
Наличные
Банкомата (рис.9.2а).
Рис.9.2. Пример класса абстрагирования данных:
а – аналитическая модель (диаграмма кооперации); б – проектная модель
(диаграмма кооперации); в – проектная модель (диаграмма классов)
198
Теперь мы можем специфицировать операции класса Наличные
Банкомата так, как показано на диаграмме кооперации проектной
модели на рис.9.26. Нужны две операции: добавитьНаличные и
выдатьНаличные. У операции выдатьНаличные один входной
параметр: суммаКВыдаче – и три выходных: выдатьПятидолларовыхКупюр, выдатьДесятидолларовыхКупюр, выдатьДвадцатидолларовыхКупюр. У операции добавитьНаличные три входных параметра: добавленоПятидолларовых
Купюр, добавленоДесятидолларовыхКупюр и добавлено
ДвадцатидолларовыхКупюр.
Вот
как
специфицируются
операции:
выдатьНаличные(in суммаКВыдаче,
out выдать ПятидолларовыхКупюр,
out выдатьДесятидолларовыхКупюр,
out выдатьДвадцатидолларовыхКупюр)
добавитьНаличные(in добавлено
ПятидолларовыхКупюр,
in добавленоДесятидолларовыхКупюр,
in добавленоДвадцатидолларовыхКупюр)
Диаграмма классов изображена на рис.9.2в.
Объекты этого класса поддерживают инвариант: общая сумма
наличных в банкомате должна равняться суммарному значению
достоинств всех купюр:
имеющаясяСумма = 5 * пятидолларовыеКупюры + .
+ 10 * десятидолларовыеКупюры + 20 * двадцатидолларовыеКупюры
Недостаток наличных – это ошибка, которую нужно
распознавать. Обычно такие ошибки обрабатываются как
исключения.
9.4. Классы интерфейса устройства
Одной из важных целей при разработке программной системы
является сокрытие от пользователей информации о модификациях
реальных устройств ввода/вывода. Типичный пример такого
изменения – это замена одного устройства другим с той же
функциональностью, но иным аппаратным интерфейсом. Разумеется,
199
крайне нежелательно, чтобы подобное изменение затронуло всех
пользователей. Решается проблема посредством классов интерфейса
устройства, которые предоставляют виртуальный интерфейс,
скрывающий детали общения с реальным устройством.
С помощью класса интерфейса устройства концепция сокрытия
информации применяется для инкапсуляции проектного решения о
том, как осуществляется интерфейс с конкретным устройством
ввода/вывода. С этой целью разрабатывается класс виртуального
интерфейса, в котором скрываются все детали реального интерфейса.
Пользователи могут обратиться к устройству только с помощью
виртуального интерфейса, состоящего из набора операций. В случае
замены устройства и модификации реального интерфейса
пользователи виртуального интерфейса ничего не узнают, поскольку
спецификация операций остается прежней. Надо лишь изменить
реализацию этих операций. Таким образом, пользователи класса
изолированы от модификаций реальной аппаратуры.
Объекты интерфейса устройств определяются на этапе
аналитического моделирования путем применения критериев
разбиения на объекты. Такие программные объекты взаимодействуют
с физическими устройствами, внешними по отношению к системе. На
этапе проектирования классов разрабатываются все классы
интерфейса устройств.
9.4.1. Проектирование операций классов интерфейса
устройств.
Экземпляры
класса
интерфейса
устройства
взаимодействуют с реальным устройством и предоставляют операции
для считывания и записи.
В классе интерфейса устройства всегда есть операция
инициализировать. Когда создается объект класса, эта операция
вызывается с целью инициализации устройства и всех внутренних
переменных объекта. Что касается других операций, то они зависят
от конкретного устройства. Например, в интерфейсе устройства
ввода, скорее всего, будет операция читать, а в интерфейсе
устройства вывода – операция писать. В классе же,
предназначенном для работы с устройством ввода/вывода, вероятно,
будут обе операции.
9.4.2. Классы интерфейса устройства ввода. В классе
интерфейса устройства ввода необходима операция инициализировать, предназначенная для инициализации устройства и установки
200
внутренних переменных класса. Для устройства ввода обычно
требуется операция читать. Реализация этих операций зависит от
характеристик конкретного устройства, но от пользователей
устройства она должна быть скрыта.
В качестве примера рассмотрим объект Интерфейс
Бензобака, у которого есть операции инициализировать и
читать. Аналитическая модель (рис.9.3а) показывает, что этот
объект получает запрос Читать Количество Топлива от объекта
Расход Топлива. Затем объект Интерфейс Бензобака просит
объект Бензобак сообщить, сколько бензина осталось. В проектной
модели (рис.9.36) вызывается операция читать внешнего объекта
Бензобак, которая возвращает ответ данныеБензобака. Как это
происходит на самом деле, зависит от реализации физического
интерфейса. Так или иначе, объект Интерфейс Бензобака
возвращает вызывающему объекту количествоТоплива.
Сообщение Читать
Количество
Топлива, которое
получает объект Интерфейс Бензобака в аналитической модели,
изображено в проектной модели в виде синхронного сообщения,
соответствующего
вызову
операции
читать.
Сообщение
Количество Топлива, которое в аналитической модели является
ответом на сообщение Читать
Количество
Топлива,
отображается на выходной параметр количествоТоплива
операции читать.
Класс Интерфейс Бензобака также изображен на диаграмме
класса
(рис.9.3в).
У
него
две
операции.
Операция
инициализировать вызывается в начальный момент. Для
операции читать указан выходной параметр количество
Топлива. Таким образом, сигнатура операции на диаграмме класса
идентична тому, что мы видим на диаграмме кооперации.
9.4.3. Классы интерфейса устройства вывода. Класс
интерфейса устройства вывода также должен иметь операцию
инициализировать. Кроме того, в нем должна быть операция
вывода. Например, в классе интерфейса устройства Дроссель есть
операция вывести. Но если устройство вывода может выполнять
различные действия, то состав операций класса необходимо
адаптировать. Так, класс Устройство Выдачи Наличных имеет
201
операцию выдатьНаличные, класс Устройство Печати Чеков
– операцию напечататьЧек, класс Дверь Лифта – операции
открыть и закрыть, а класс Мотор Лифта – операции вверх,
вниз и стоп.
Рис.9.3. Пример класса интерфейса устройства ввода:
а – аналитическая модель (диаграмма кооперации); б – проектная модель
(диаграмма кооперации); в – проектная модель (диаграмма классов)
202
Рассмотрим диаграмму кооперации из аналитической модели на
рис.9.4а.
Объект
Интерфейс
Маршрутного
Дисплея,
помеченный стереотипом «интерфейс устройства вывода», получает
сообщения Средняя Скорость и Средний Расход и выводит их
на Маршрутный Дисплей.
Рис.9.4. Пример класса интерфейса устройства вывода:
а – аналитическая модель (диаграмма кооперации)
В классе Интерфейс Маршрутного Дисплея (рис.9.4в) есть
две операции: вывестиСреднююСкорость (скорость) и
вывестиСреднийРасход (расход Топлива). В данном случае
объект-алгоритм Средняя
Скорость вызывает операцию
вывестиСреднююСкорость, передавая текущее значение средней
скорости. На диаграмме кооперации в проектной модели это
изображено в виде синхронного сообщения без возвращаемого
значения (рис.9.46). Соответственно объект-алгоритм Расход
Топлива
вызывает
операцию
вывестиСреднийРасход,
передавая текущее значение расхода топлива. Класс Интерфейс
Маршрутного Дисплея скрывает информацию о том, как
форматируются данные и как осуществляется взаимодействие с
физическим Маршрутным Дисплеем.
203
Рис.9.4. Пример класса интерфейса устройства вывода:
б – проектная модель (диаграмма кооперации);
в – проектная модель (диаграмма классов)
9.5. Классы, зависящие от состояния
Класс, зависящий от состояния, инкапсулирует информацию,
которая представлена на диаграмме состояний. На этапе
проектирования классов диаграмма состояний, исполняемая
зависящим от состояния объектом, отображается на таблицу
переходов состояний. Таким образом, зависящий от состояния класс
скрывает устройство этой таблицы и поддерживает текущее
состояния объекта.
Зависящий от состояния класс предоставляет операции для
доступа к таблице переходов состояний и для ее изменения. В
частности, проектируются операции для обработки входных событий,
204
вызывающих переходы состояний. Например, можно задать
отдельную операцию для каждого такого события. Это означает, что
зависящий от состояния класс проектируется специально для
конкретной диаграммы состояний. Хотелось бы, однако, чтобы
подобные классы были более обобщенными и могли использоваться
повторно.
Обобщенный зависящий от состояния класс по-прежнему будет
скрывать устройство таблицы переходов, но в нем имеется всего две
операции: обработатьСобытие и текущееСостояние. Операция
обработатьСобытие вызывается, когда поступает новое событие,
которое и передается в качестве входного параметра. Операция
текущееСостояние (она может отсутствовать) возвращает
состояние конечного автомата. Такая операция нужна лишь в тех
приложениях, где клиентам необходимо знать текущее состояние
зависящего от состояния класса. Вот сигнатуры операций:
обработатьСобытие (событие)
текущееСостояние ( ) : Состояние
Операция обработатьСобытие просматривает таблицу
переходов состояний с целью определить, что нужно сделать с новым
событием, принимая во внимание текущее состояние и заданные в
таблице условия. Таблица показывает, в какое состояние следует
перейти (если это необходимо) и какие действия одновременно
выполнить. Таким образом, операции остается изменить состояние
объекта и выполнить действия, вызвав операции тех или иных
объектов.
Другой, более гибкий подход к проектированию операции
обработатьСобытие состоит в том, чтобы не выполнять действие
самостоятельно, а вернуть его в выходном параметре. Тогда
сигнатура принимает вид:
обработатьСобытие (in событие, out действие)
Такой зависящий от состояния класс оказывается достаточно
общим для инкапсуляции любой таблицы переходов. Устройство
таблицы зависит от приложения и определяется в момент создания
или инициализации объекта класса. Пример зависящего от состояния
класса в системе круиз-контроля – это класс Управление
Калибровкой. На диаграмме кооперации из аналитической модели
(рис.9.5а)
показан
объект
Управление
Калибровкой,
исполняющий диаграмму состояния на рис.9.56.
205
Рис.9.5. Пример зависящего от состояния управляющего класса:
а – аналитическая модель (диаграмма кооперации); б – аналитическая модель
(диаграмма состояний); в – проектная модель (диаграмма кооперации); г –
проектная модель (диаграмма классов)
206
Объект Управление Калибровкой получает события
Начать Калибровку и Прекратить Калибровку от двух
объектов Интерфейс Кнопки Калибровки. Если получено
сообщение Начать Калибровку (событие Ca1.1), то диаграмма
состояний (рис.9.56) показывает, что должно быть выполнено
действие Начать (Са1.2). В результате объект Управление
Калибровкой
посылает
сообщение
Начать
объекту
Калибровочная Константа, который выполняет действие. Если
же получено сообщение Прекратить Калибровку (событие
Са2.1), то из диаграммы состояний следует, что нужно выполнить
действие Прекратить (Са2.2). Теперь объект Управление
Калибровкой отправляет сообщение Прекратить объекту
Калибровочная Константа.
В проектной модели (рис.9.5в и 9.5г) сообщения Начать
Калибровку и Прекратить Калибровку отображаются на
вызовы операции обработать Событие класса Управление
Калибровкой, которой в качестве входного параметра передаются
соответственно события началоКалибровки и прекращениеКалибровки. Действия начать и прекратить превращаются в
операции объекта абстрагирования данных Калибровочная
Константа и вызываются операцией обработатьСобытие
объекта Управление Калибровкой.
9.6. Классы, скрывающие алгоритмы
Такие классы скрывают алгоритмы, применяемые в предметной
области; они типичны в системах реального времени, а также в
научных и инженерных приложениях. Как правило, такой класс
скрывает не только алгоритм, но и локальные данные, необходимые
для его работы.
Пример скрывающего алгоритм класса – это класс Средняя
Скорость (рис.9.6), который скрывает алгоритм вычисления
средней скорости автомобиля в системе круиз-контроля и
мониторинга. В нем есть операция сброс для обнуления внутренних
данных и операция вычислить для расчета средней скорости
движения. Обе эти операции вызывают операцию вывести объекта
Интерфейс Маршрутного Дисплея, передавая среднюю
207
Скорость в качестве параметра (рис.9.6б). Диаграмма класса
изображена на рис.9.6в.
.
Рис.9.6. Пример класса-алгоритма: а – аналитическая модель (диаграмма
кооперации); б – проектная модель (диаграмма кооперации); в –проектная
модель (диаграмма классов)
208
9.7. Классы интерфейса пользователя
Класс интерфейса пользователя скрывает от других классов
детали человеко-машинного интерфейса. В зависимости от
приложения интерфейс пользователя может быть очень простым
(например, в виде командной строки) или весьма сложным
(графический интерфейс пользователя – ГИП). Чтобы реализовать
интерфейс командной строки, достаточно одного класса, а для
графического интерфейса требуется, как правило, несколько.
Низкоуровневые классы интерфейса пользователя – это элементы
управления, находящиеся в библиотеках компонентов: окна, меню,
кнопки и диалоги. Высокоуровневые классы пользовательского
интерфейса часто составляются из таких классов.
В аналитической модели акцент делается на идентификации
составных классов пользовательского интерфейса и сборе
информации о том, какие данные пользователь должен вводить, а
какие ему надо показать. На этой же стадии можно проектировать
выводимые формы. В проектной модели приложения с графическим
интерфейсом нужно определить классы для каждой экранной формы
В качестве примера рассмотрим класс Интерфейс Клиента
(рис.9.7а). Это основной интерфейс клиента банкомата. В классе
Интерфейс
Клиента есть операции для каждого окна,
используемого для диалога с пользователем: вывести ОкноПИНкода, вывестиОкноСнятияДенёг, вывестиОкноПеревода
Денег и вывестиОкноСправки – а также для вывода меню:
вывестиМеню. Имеется операция для работы с маленьким окном,
предназначенным для вывода приглашений и сообщений
пользователю, но не для ввода данных. Параметром такой операции
служит идентификатор выводимого сообщения. Каждое окно
показывает некоторую информацию, а затем, возможно, ожидает
ввода данных, которые возвращаются в виде выходного параметра.
Класс Интерфейс Клиента – это составной класс,
включающий несколько низкоуровневых классов, как показано на
рис.9.76. В него входят классы для каждого окна, необходимого при
организации интерфейса с клиентом: окно Главного Меню,
Окно ПИН-кода, Окно Снятия Денег, Окно Перевода
Денег, Окно Справки и Окно Приглашения.
209
Рис.9.7. Пример класса интерфейса пользователя:
а – класс интерфейса пользователя с операциями;
б – структура составного класса пользовательского интерфейса
9.8. Классы бизнес-логики
Класс бизнес-логики определяет логику принятия решения при
обработке запроса клиента, специфичную для данного приложения.
Он предназначен для инкапсуляции бизнес-правил, которые
способны изменяться независимо друг от друга. Обычно во время
выполнения объект бизнес-логики обращается к различным
сущностным объектам.
Примером такого класса может служить класс Менеджер
210
Транзакций Снятия (рис.9.8), где инкапсулированы правила
обработки запроса на снятие денег.
Рис.9.8. Пример класса бизнес-логики: а – аналитическая модель (диаграмма
кооперации); б – проектная модель (диаграмма кооперации); в – проектная
модель (диаграмма классов)
211
В нем есть операции инициализировать, снять,
подтвердить, и отменить. Операция инициализировать
вызывается на этапе инициализации, операция снять – для снятия
денег со счета клиента, операция подтвердить – для подтверждения факта успешного проведения транзакции, а операция
отменить – в случае, когда транзакция закончилась неудачно, в
частности если банкомат не выдал наличные. При определении
операций необходимо тщательно проанализировать диаграмму
кооперации Банковский Сервер из аналитической модели
(рис.9.8а) и описание последовательности сообщений, где приведены
детальные сведения о каждом сообщении. По результатам такого
анализа строится диаграмма кооперации для проектной модели
(рис.9.86) и диаграмма классов (рис.9.8в).
9.9. Классы-обертки базы данных
В аналитической модели представлены сущностные классы,
которые инкапсулируют данные. В ходе проектирования нужно
решить, должна ли информация храниться в самом сущностном
классе или же в базе данных. В первом случае применяются классы
абстрагирования данных, которые инкапсулируют структуры данных,
а во втором – классы-обертки базы данных, скрывающие детали
реализации доступа к базе.
На сегодняшний день наиболее широко распространены
реляционные базы данных, так что класс-обертка предоставляет
объектно-ориентированный интерфейс к такой базе. При этом
необходимо отобразить сущностные классы из статической модели
на структуры базы данных.
Атрибуты сущностного класса становятся отношениями в базе,
а операции доступа к атрибутам встраиваются в класс-обертку. Для
отображения сущностных классов на отношения требуется
систематический подход, поскольку отношения – это плоские
структуры, а сущностные классы необязательно являются таковыми.
Необходимо определить первичные ключи, а ассоциации, явно представленные в диаграмме классов, следует отобразить на внешние
ключи.
Класс-обертка скрывает детали доступа к данным, хранящимся
в таблицах базы, а значит, и все операторы языка SQL. Обычно один
212
класс соотносится с одной таблицей, но возможны также классыобертки и для представлений, то есть соединения двух или более
таблиц.
Рис.9.9. Пример класса-обертки базы данных:
а – аналитическая модель; б – проектная модель
213
Пример класса-обертки базы данных приведен на рис.9.9. В
банковской системе все устойчивые данные хранятся в базе, поэтому
сущностные классы Банковского Сервера отображаются как на
отношение базы данных, так и на класс-обертку. Рассмотрим, к
примеру, сущностный класс Дебетовая
Карточка из
аналитической модели (рис.9.9а). Поскольку информация с карточки
хранится в реляционной базе данных, этому классу соответствует
отношение в базе. Атрибуты класса отображаются на атрибуты
отношения. Кроме того, надо указать первичный ключ; мы выбрали
атрибут идентификатор Карточки, так как он однозначно
определяет строку отношения (рис.9.96). Требуется также внешний
ключ для представления ассоциации Клиент
владеет
Дебетовой Карточкой.
Внешний ключ является первичным в таблице, связанной с
данной. Он соответствует ассоциации между классами и
используется для навигации между таблицами. Первичный ключ для
отношения Клиент – это НСС Клиента (номер социального
страхования), так что именно он становится внешним в отношении
Дебетовая Карточка.
Необходимо спроектировать класс-обертку Дебетовая
Карточка (рис.9.96), в котором будут следующие операции:
создать,
проверить,
проверитьСуточныйЛимит,
обнулитьИтог, обновить, читать и удалить. Эти операции
инкапсулируют операторы SQL для доступа к отношению
Дебетовая Карточка. Обратите внимание, что атрибуты класса
разрешается обновлять независимо, поэтому для каждого атрибута
имеется своя операция обновить. Вызов любой операции приводит к
выполнению некоторого оператора SQL.
9.10. Внутренние программные классы
Внутренние
программные
классы
скрывают
решения
разработчика, которые могут со временем измениться. Обычно они
создаются на поздних стадиях процесса проектирования. Определить
их из аналитической модели, относящейся к предметной области,
невозможно.
Внутренние программные классы инкапсулируют различные
структуры данных (стеки, очереди, таблицы данных), выбранные
проектировщиком. Примерами внутренних классов являются также
214
классы-разъемы.
9.11. Применение наследования при проектировании
Наследование полезно при проектировании двух похожих
классов, то есть таких, у которых много общих черт. На этапе
проектирования архитектуры его нужно иметь в виду, чтобы
впоследствии на этапах детального проектирования и кодирования
можно было повторно использовать тот же или слегка видоизмененный код. Наследование задействуют и с целью сделать проект
более удобным для сопровождения – такое применение оченъ
упрощает инкрементную модификацию программы.
9.11.1. Иерархии классов. Иерархии классов (называемые также
иерархиями
обобщения/специализации
или
иерархиями
наследования) допустимо разрабатывать сверху вниз, снизу вверх или
в обоих направлениях. При разработке сверху вниз проектируется
класс, который включает характеристики, общие для всех его
наследников. В ходе специализации этого класса формируются
подклассы со специфическими особенностями. Можно поступать и
наоборот, если в первоначальном проекте есть классы, имеющие как
общие черты (операции или атрибуты), так и некоторые отличия. В
таком случае общие свойства переносятся в суперкласс и
наследуются подклассами.
Следует отметить, что при использовании наследования
внутреннее устройство родительских классов видимо подклассам,
такой вид повторного применения называется прозрачным ящиком,
нарушающим концепцию инкапсуляции (сокрытия-информации).
Реализация класса-потомка тесно связана с реализацией
родительского класса, что бывает причиной затруднений в глубоких
иерархиях наследования. Изменение класса, расположенного на
верхнем уровне иерархии, может затронуть всех его потомков. По
этой причине стоит ограничить глубину иерархии классов.
9.11.2. Абстрактные классы. У абстрактного класса не может
быть экземпляров, поэтому он используется как шаблон для создания
классов, а не объектов. Иными словами, он способен выступать
только в роли суперкласса, который определяет общий интерфейс для
всех своих подклассов. Абстрактная операция – это операция,
которая объявлена, но не реализована в абстрактном классе.
Абстрактный класс должен иметь хотя бы одну абстрактную
215
операцию.
Абстрактный класс оставляет реализацию некоторых операций
своим подклассам. Подкласс в состоянии реализовывать операции,
придерживаясь
интерфейса, заданного своим абстрактным
родителем. Различные подклассы абстрактного класса могут
определять разные реализации одной и той же операции. Таким
образом, абстрактный класс задает интерфейс в форме набора
абстрактных операций. Подклассы реализуют этот интерфейс и
расширяют его, добавляя собственные операции.
Некоторые операции разрешается осуществлять в самом
абстрактном классе, особенно тогда, когда их реализация в
некоторых или всех подклассах должна быть одинаковой. Иногда
абстрактный класс определяет реализацию операции по умолчанию.
В подклассе эту операцию можно при необходимости заместить. Так
делают, когда некий подкласс должен обрабатывать особый случай,
для которого реализация по умолчанию не годится.
9.11.3. Полиморфизм и динамическое связывание. Слово
«полиморфизм» в переводе с греческого означает «многообразие
форм». В объектно-ориентированном проектировании полиморфизм
используется, когда в различных классах должна существовать
операция с одним и тем же именем. Ее спецификация во всех классах
одинакова, но реализация может быть различной: это дает
возможность во время выполнения использовать вместо одного
объекта другой с тем же интерфейсом.
Динамическое
связывание
применяется
совместно
с
полиморфизмом для ассоциирования запроса с объектом и одной из
его операций во время исполнения. При статическом связывании
(типичный случай в процедурных языках) ассоциация запроса с
операцией устанавливается во время компиляции, ее нельзя впоследствии изменить. При динамическом связывании, напротив,
запрос может сначала выполняться операцией одного объекта, а
потом операцией другого объекта.
9.12. Примеры наследования
Ниже
мы
приведем
три
примера
наследования,
иллюстрирующих применение суперклассов и подклассов,
полиморфизма и динамического связывания, а также абстрактных
классов.
216
9.12.1. Примеры суперклассов и подклассов. В банковской
системе класс Счет имеет два атрибута: номерСчета и баланс.
Поскольку счета нужно открывать и закрывать, дебетовать и
кредитовать, а также считывать баланс, то определяются следующие
операции:
§
§
§
§
§
открыть (номерСчета : Integer)
закрыть ()
читатьБаланс () : Real
кредитовать (сумма : Real)
дебетовать (сумма : Real)
Банк работает с двумя видами счетов: чековыми и
сберегательными. Ситуация выглядит вполне подходящей для
применения наследования: нужно создать обобщенный суперкласс
счета и его специализированные подклассы для чековых и
сберегательных счетов. На данном этапе следует ответить на такие
вопросы: какие обобщенные атрибуты и операции должны быть в
суперклассе и какие специализированные атрибуты – у
сберегательного и чекового счета? Должен ли суперкласс быть
абстрактным?
Но прежде чем отвечать, надо выявить сходство и различие
между чековыми и сберегательными счетами. Сначала рассмотрим
атрибуты. Ясно, что у любого счета есть номер и баланс,
следовательно, эти атрибуты можно обобщить и поместить в класс
Счет. С другой стороны, в данном банке на сберегательные счета
начисляются проценты, а на чековые - нет. Нужно знать сумму
начисленных процентов, поэтому в классе Сберегательный Счет
объявляется атрибут накопительныйПроцент. Кроме того, со
сберегательного счета разрешается безвозмездно снимать деньги не
более трех раз в месяц, что приводит к объявлению атрибута
счетчикДебетований. Еще объявляются два статических атрибута
класса, их значение одинаково для всех объектов данного класса:
максимальноеЧислоБесплатныхДебетований (сколько раз в
месяц можно снимать деньги со счета, не оплачивая банковские
услуги; начальное значение равно 3) и платаБанку (сумма,
выделяемая банку за каждое снятие денег сверх лимита; равна 1,5
доллара). Кроме того, в случае чековых счетов желательно знать,
217
какая сумма была положена в последний раз. Поэтому в классе
Чековый Счет объявляется атрибут суммаПоследнегоВклада.
И для Чекового Счета, и для Сберегательного Счета
нужны те же операции, что и для обобщенного класса Счет:
открыть,
закрыть,
читатьБаланс,
кредитовать,
дебетовать. Интерфейс этих операций определен в суперклассе
Счет и наследуется обоими подклассами. Операции открыть,
закрыть и кредитовать выполняются одинаково и для чековых,
и для сберегательных счетов, поэтому их реализацию можно
определить в классе Счет и унаследовать в подклассах. Что касается
операции дебетовать, то Чековый Счет в состоянии унаследовать
ее
реализацию
от
класса
Счет.
Но
для
Сберегательного Счета она осуществляется иначе: помимо
уменьшения баланса необходимо еще увеличить счетчик
Дебетований и вычесть из баланса платуБанку, если число
дебетований превысило лимит. Нужна также дополнительная
операция обнулитьСчетчикДебетований, которая сбрасывает
значение счетчикаДебетований в начале каждого месяца.
Рис.9.10. Пример суперкласса и подклассов
218
На первый взгляд кажется, что операция читатьБаланс для
обоих классов одинакова, но более внимательное рассмотрение
показывает, что это не так. При обращении к чековому счету
требуется получить баланс и сумму последнего вклада, а для
сберегательного счета нужен баланс и накопительный процент.
Следовательно, необходимо ввести разные операции чтения. В
суперклассе определяется обобщенная операция читатьБаланс,
которая наследуется и чековым, и сберегательным счетом. Затем в
класс Сберегательный Счет добавляется операция читать
НакопительныйПроцент, а в класс Чековый Счет – операция
читатьСуммуПоследнегоВклада.
Проект иерархии класса Счет показан на рис.9.10 и
прокомментирован ниже.
Проект подкласса «Чековый Счет»
1. Атрибуты подкласса Чековый Счет:
– наследует атрибуты номерСчета и баланс. Оба атрибута
объявлены защищенными в суперклассе Счет, поэтому они
видимы в подклассах;
– добавляет атрибут суммаПоследнегоВклада.
2. Операции подкласса Чековый Счет:
–
наследует
операции
открыть,
закрыть,
читатьБаланс, кредитовать и дебетовать;
– добавляет операцию читатьСуммуПоследнегоВклада ()
: Real
Проект подкласса «Сберегательный Счет»
1. Атрибуты подкласса Сберегательный Счет:
– наследует атрибуты номерСчета и баланс;
– добавляет атрибуты накопительныйПроцент и счетчик
Дебетований;
– добавляет статические атрибуты класса максимальное
ЧислоБесплатныхДебетований и платаБанку. Статические
атрибуты в UML подчеркиваются (см. рис.9.10).
2. Операции подкласса Сберегательный Счет:
– наследует спецификацию и реализацию операций открыть,
закрыть, читатьБаланс и кредитовать;
– наследует спецификацию операции дебетовать, но
219
переопределяет ее реализацию: уменьшает баланс счета и взимает
плату банку, если превышен месячный лимит бесплатных
дебетований;
– добавляет операции:
– начислитьПроцент (процентнаяСтавка : Real).
Ежедневное начисление процентов;
– читатьНакопительныйПроцент () : Real;
– обнулитьСчетчикДебетований
(). Сбрасывает
счетчикДебетований в нуль в начале каждого месяца.
9.12.2. Пример полиморфизма и динамического связывания.
Рассмотрим теперь создание объектов этих классов, а также пример
полиморфизма и динамического связывания.
begin
private account : Счет;
Предложить
клиенту
ввести
тип
счета
и
снимаемую сумму
if клиент выбрал чековый счет
then -- Записать в переменную account
чековый счет клиента.
. . .
account := customerCheckingAccount;
. . .
elseif клиент выбрал сберегательный счет
then -- Записать в переменную account
сберегательный счет клиента.
. . .
account := customerSavingsAccount;
endif;
. . .
-- Дебетовать счет account, который может быть
чековым или сберегательным.
account.дебетовать (сумма);
. . .
end;
В этом примере, если выбран чековый счет, то в переменную
account записывается объект класса Чековый Счет. При
выполнении оператора account. дебетовать вызывается операция
Чекового Счета. Если же указан сберегательный счет, то оператор
220
account.дебетовать
приведет
к
вызову
операции
Сберегательного Счета. Для чековых и сберегательных счетов
выполняются разные варианты операции дебетовать, в частности
для сберегательного счета из баланса дополнительно вычитается
плата банку, если превышен месячный лимит дебетований.
Следует отметить, что объекту типа Счет можно присвоить
объект типа Чековый Счет или Сберегательный Счет, но
обратное неверно. Дело в том, что каждый Чековый Счет является
Счетом, равно как и каждый Сберегательный Счет является
Счетом, однако нельзя сказать, будто каждый Счет является
Сберегательным, он может быть и Чековым.
9.12.3. Пример наследования абстрактному классу. Рассмотрим
класс Техническое Обслуживание из системы круизконтроля и мониторинга. Классы Замена Масла, Замена
Воздушного
Фильтра
и
Станционное
ТО
можно
проектировать отдельно, но, если сделать их подклассами класса
Техническое Обслуживание, кода придется писать гораздо
меньше.
Иерархия
обобщения/специализации
для
класса
Техническое Обслуживание показана на рис.9.11.
Рис.9.11. Пример абстрактного суперкласса и подклассов
221
Суперкласс
Техническое
Обслуживание
является
абстрактным, в нем есть две операции: сбросить и проверить.
Обе операции используют инкапсулированный тип данных
начальныйПробег, который равен общему пути, пройденному к
моменту предыдущего технического обслуживания. Операция
сбросить вызывает операцию читать объекта Путь, которая
возвращает значение полного Пробега, а затем присваивает это
значение атрибуту начальный Пробег. Операция проверить
абстрактная, она специфицирована в классе Техническое
Обслуживание, но реализована только в его подклассах.
Любой подкласс, допустим Замена Масла, наследует
структуру класса Техническое Обслуживание, в том числе
определение атрибута начальныйПробег и полное определение
(спецификацию и реализацию) операции сбросить, а также
спецификацию абстрактной операции проверить. Но реализацию
последней операции подкласс Замена Масла должен предоставить
сам. Для нее требуется указать расстояние, после которого масло
надо менять (скажем, каждые 5 000км), и сообщение, выводимое,
когда пробег с момента последней замены превысил заданный порог.
Нужно
также
задать
операцию
вывестиСообщениеО
ЗаменеМасла, которая вызывается для объекта Интерфейс
Индикатора ТО. Помимо этого, операция проверить вызывает
операцию читать объекта Путь для получения текущего полного
Пробега. Она вычитает из него значение начального Пробега,
чтобы выяснить, не прошла ли машина 5 000 км и не пора ли,
следовательно, менять масло.
9.13. Спецификация интерфейса класса
В спецификации интерфейса класса определяется интерфейс
скрывающего информацию класса, в том числе его операции.
Спецификация должна включать:
– описание информации, скрываемой классом, например
инкапсулированную структуру данных (если речь идет о классе
абстрагирования данных) или интерфейс устройства;
– критерий, на основании которого был выделен данный класс;
– предположения, сделанные при специфицировании класса:
могут разные задачи получать одновременный доступ к операциям
222
объекта этого класса или доступ должен быть строго
последовательным;
–
предположительные
модификации,
побуждающие
проектировать с учетом возможных изменений;
– суперкласс (если есть);
– унаследованные операции (если есть);
– операции класса. Для каждой операции необходимо
определить:
выполняемую функцию;
предусловие (условие, которое должно выполняться перед
вызовом операции);
постусловие (условие, которое должно выполняться после
вызова операции);
инвариант (условие, которое должно выполняться всегда);
входные параметры;
выходные параметры;
вызываемые операции других классов.
Спецификацию интерфейса класса можно представить в
описательной или табличной форме. Ниже приводится пример
описательной документации.
9.13.1. Пример спецификации интерфейса класса. Для
иллюстрации возьмем класс, абстрагирующий данные о датчиках и
приводах (рис.9.12).
Рис.9.12. Пример класса, для которого составляется спецификация интерфейса
223
Скрывающий информацию класс. Хранилище Показаний
Датчиков и Приводов.
Скрываемая информация. Инкапсулирует структуры данных,
связанных с датчиками и приводами. Хранит текущие показатели
датчиков и приводов.
Критерий выделения класса. Класс абстрагирования данных.
Предположения. Операции могут вызываться одновременно
несколькими задачами.
Предположительные модификации. Сейчас поддерживает
только булевские датчики и приводы. Возможно расширение для
работы с аналоговыми устройствами.
Суперкласс. Нет.
Унаследованные операции. Нет.
Предоставляемые операции:
1.
читатьДатчик
(in
идДатчика,
out
значениеДатчика)
Функция. Возвращает текущее значение указанного датчика.
Предусловие. Значение датчика ранее было обновлено.
Инвариант. Значение датчика не изменяется.
Постусловие. Прочитано значение датчика.
Входные параметры. идДатчика.
Выходные параметры. значениеДатчика.
Вызываемые операции. Нет.
2.
обновитьПривод
(in
идПривода,
in
значениеПривода)
Функция. Обновляет значение указанного привода перед
выводом.
Предусловие. Привод существует.
Постусловие. Значение привода обновлено.
Входные параметры. идПривода, значениеПривода.
Выходные параметры. Нет.
Вызываемые операции. Нет.
3. обновитьДатчик(in
идДатчика,in
значение
Датчика)
Функция. Заменяет значение указанного датчика новым
показанием, прочитанным с внешнего устройства.
Предусловие. Датчик существует.
Постусловие. Значение датчика обновлено.
224
Download