Конспект лекций по курсу «Объектно-ориентированный анализ и проектирование»

Лекция 1. Основы программной инженерии
Лекция 2. МОДЕЛИ И ИХ РОЛЬ В СОЗДАНИИ СИСТЕМ. ОБЪЕКТНАЯ МОДЕЛЬ
Лекция 3. Унифицированный язык моделирования (Unified Modelling Language)
Лекция 4. Объектный язык ограничений (OCL)
Лекция 5. Моделирование бизнес-процессов
Лекция 6. ОПРЕДЕЛЕНИЕ ТРЕБОВАНИЙ К ПРОГРАММНОМУ ОБЕСПЕЧЕНИЮ
Лекция 7. Анализ и проектирование программного обеспечения. Анализ ПО
Лекция 8. Анализ и проектирование программного обеспечения. Проектирование ПО
Лекция 9. Проектирование баз данных
Лекция 10. Образцы проектирования
Лекция 11. Технология создания программного обеспечения. Rational Unified Process (RUP)

Лекция 1. Основы программной инженерии

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

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

Проектирование ПО - это процесс создания спецификаций ПО на основе исходных требований к нему.

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

ПО можно разбить на два класса: «малое» и «большое».

«Малое» программное обеспечение имеет следующие характеристики:

«Большое» программное обеспечение имеет 2-3 или более характеристик из следующего перечня:

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

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

С конца 60-х годов прошлого века до сегодняшних дней продолжается так называемый «кризис ПО». Выражается он в том, что большие проекты выполняются с превышением сметы расходов и/или сроков отведенных на разработку, а разработанное ПО не обладает требуемыми функциональными возможностями, имеет низкую производительность и качество. По результатам исследований американской индустрии разработки ПО, выполненных в 1995 году Standish Group (www.standishgroup.com), только 16% проектов завершились в срок, не превысили запланированный бюджет и реализовали все требуемые функции и возможности. 53% проектов завершились с опозданием, расходы превысили запланированный бюджет, требуемые функции не были реализованы в полном объеме. 31% проектов были аннулированы до завершения. Для двух последних категорий проектов бюджет среднего проекта оказался превышенным на 89%, а срок выполнения - на 122%. В последние годы процентное соотношение трех перечисленных категорий проектов незначительно изменяется в лучшую сторону.

1995

1996

1998

2000

2004

2006

2009

31% отменены

40%

28%

23%

18%

19%

24%

53% ущербны

33%

46%

49%

53%

46%

44%

16% успешны

27%

26%

28%

29%

35%

32%

Причины неудач:

При планировании проектов зачастую по тем или иным причинам устанавливаются невыполнимые сроки, закладываются недостаточные ресурсы. Таким образом, возникают безнадежные проекты (death march projects). Признаки безнадежного проекта:

Другой причиной неверного планирования является заблуждение относительно производительности проектировщиков. В большом проекте общая производительность группы разработчиков не равна сумме производительностей отдельных членов группы (посчитанной как если бы они работали в одиночку). Об этом в книге «Мифический человеко-месяц» пишет Фредерик Брукс. Выводы Брукса:

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

Особенности современных проектов ПО:

Разработка ПО имеет следующие специфические особенности:

Путем выхода из кризиса ПО стало создание программной инженерии (software engineering). Инженерия ПО (software engineering) - совокупность инженерных методов и средств создания ПО. Фундаментальная идея программной инженерии: проектирование ПО является формальным процессом, который можно изучать и совершенствовать.

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

Этапы становления и развития SE:

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

Основным понятием программной инженерии является понятие жизненного цикла ПО. Жизненный цикл ПО (software lifecycle) - это период времени, который начинается с момента принятия решения о необходимости создания ПО и заканчивается в момент его полного изъятия из эксплуатации.


Основной нормативный документ, регламентирующий ЖЦ ПО - стандарт ISO/IEC 12207 "Information Technology - Software Life Cycle Processes" (ГОСТ Р ИСО/МЭК 12207-99). В рамках технологий создания ПО понятие ЖЦ уточняется, но указанные стандарты не нарушаются. Процесс стандартизации ведётся довольно интенсивно. Стандарт ISO/IEC 12207 имеет несколько редакций с 1995 по 2008 год.

С точки зрения статической структуры ЖЦ является совокупностью процессов ЖЦ.

Процесс ЖЦ - набор взаимосвязанных действий, преобразующих некоторые входные данные и ресурсы в выходные.

Каждый процесс характеризуется задачами, методами их решения, действующими лицами, результатами. Процессы ЖЦ протекают параллельно. Каждый процесс разделен на набор действий, каждое действие - на набор задач. Каждый процесс, действие или задача инициируется и выполняется по мере необходимости, причем не существует заранее определенных последовательностей выполнения. Группы процессов ЖЦ (ISO/IEC 12207:1995):

Для ознакомления приведем содержание процессов ЖЦ.

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

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

Процесс разработки включает в себя следующие действия: подготовительную работу; анализ требований к ПО; проектирование архитектуры ПО; детальное проектирование ПО; кодирование ПО; тестирование ПО; интеграцию ПО; установку ПО; приемку ПО. Действующие лица: разработчик, заказчик. Задачи разработки: выбор модели ЖЦ ПО и согласование с заказчиком; определение требований к ПО (функциональных и нефункциональных); определение состава компонентов ПО и создание документации по каждому компоненту; моделирование и спецификация компонент ПО; планирование интеграции компонент; создание исходных текстов компонент; поиск и исправление ошибок в исходных текстах и документации; сборка ПО; развертывание ПО; оценка результатов.

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

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

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

Процесс управления конфигурацией в себя следующие действия: подготовительную работу; создание базы знаний о ПО (конфигурации); контроль за конфигурацией; учет состояния конфигурации; оценку конфигурации; управление выпуском и поставку ПО. Конфигурация ПО - это совокупность сведений о его функциональных и физических характеристиках на всех стадиях ЖЦ ПО. Основная задача управления конфигурацией: организация, систематический учет и контроль внесения изменений в ПО.

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

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

Процесс аттестации состоит в определении полноты соответствия разработанного ПО требованиям заказчика. Основная задача аттестации - оценка достоверности тестирования ПО. Как правило, для аттестации привлекают независимых экспертов.

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

Процесс аудита состоит в определении полноты соответствия проекта условиям договора.

Процесс разрешения проблем предусматривает анализ и разрешение проблем, возникающих в течение ЖЦ ПО.

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

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

Процесс усовершенствования предусматривает оценку, измерение, контроль и усовершенствование процессов ЖЦ ПО. Основная задача усовершенствования - повышение производительности труда.

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

В обновленной версии ISO/IEC 12207:2008 рассматриваются больше процессов ЖЦ:

Группа процессов договора: приобретение и поставка.

Группа организационных процессов: управление моделью ЖЦ, управление инфраструктурой, управление проектным портфолио, управление кадрами, управление качеством.

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

Группа процессов ПО (software-specific processes) включает три подгруппы: процессы создания ПО (группа из 7 пр.); процессы поддержки ПО (8 процессов); процессы повторного использования ПО (3 процесса).

Сравнение номенклатуры процессов ЖЦ в двух версиях стандарта ISO/IEC 12207 отражает происходящее в программной инженерии накопление опыта.

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

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

В любой модели ЖЦ рассматривается как совокупность стадий ЖЦ.

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

Модели ЖЦ:

Схема каскадной модели ЖЦ:

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

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

Проектирование охватывает процессы: разработку архитектуры ПО, разработку структур программ в его составе и их детальную спецификацию.

Реализация или кодирование включает процессы создания текстов программ на языках программирования.

На этапе тестирования производится собственно тестирование, а также отладка и оценка качества ПО.

Ввод в действие - это развертывание ПО на целевой вычислительной системе, обучение пользователей и т.п.

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

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

Обратите внимание на схожесть стадий в водопадной модели с перечнем технических процессов в ISO/IEC 12207:2008.

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

Неизбежные возвраты на предыдущие стадии в каскадной модели

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

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

Схема модели ЖЦ, основанной на формальных преобразованиях

Особенности итерационных моделей:

Схема пошаговой итерационной модели ЖЦ

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

Схема спиральной модели ЖЦ

Особенности спиральной модели:

Достоинства итерационных моделей:

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

Проблемой современной программной инженерии являются «тяжелые» процессы. Характеристики «тяжелого» процесса:

Противоположностью «тяжелого» процесса является «легковесный» процесс - основа быстрой или гибкой разработки ПО (agile software development). Гибкая разработка ориентируется на эффективную коммуникацию между разработчиками, высокую квалификацию разработчиков и другие факторы, позволяющие сократить расходы на «бюрократию».

Манифест гибкой разработки:

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

Принципы быстрой разработки:


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

Большая «тяжесть» процесса подходит для проектов с большей критичностью.

Под критичностью понимаются масштабы последствий отказа разрабатываемого ПО. Уровни критичности:

Примеры: I) данные выданы не в виде диаграммы, а в виде таблицы; II) «синий экран смерти»; III) взрыв ракеты «Ариан-5» 4 июня 1996 года (500 000 000$) и массовое отключение электричества в Северной Америке 14 августа 2003 г. (4-10 Гига$); IV) неправильная работа медицинской рентгеновской установки Therac-25 (3 смерти).

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

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

Дисциплина, умение и понимание противостоят процессу, формальности и документированию.

Процесс не заменит дисциплины разработчика, так как без нее работа будет вестись неэффективно. Известно понятие «забастовка по-итальянски», когда люди следуют букве инструкций, руководств, но фактически ничего не делают. Умение ценится выше формальности, так как отсутствующие навыки не могут быть заменены никаким справочным руководством по процессу. Наличие самой полной документации не равноценно пониманию. Знание в голове ценятся выше, чем знания на бумаге.

Потеря эффективности в некритических видах деятельности вполне допустима.

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

Литература к лекции 1

Брукс Ф. Мифический человеко-месяц или как создаются программные системы. - СПб.: Символ-Плюс, 1999

Вендров А.М. Проектирование программного обеспечения экономических информационных систем: Учебник. - М.: Финансы и статистика, 2005

Коберн А. Быстрая разработка программного обеспечения. - М.: ЛОРИ, 2002

Соммервилл И. Инженерия программного обеспечения. 6-е издание.: Пер. с англ.: - М.: Вильямс, 2002

Жоголев Е. А. Технология программирования - М.: Научный мир, 2004

Кулямин В. В. Технологии программирования. Компонентный подход. - М.: Бином. Лаборатория знаний, 2007 [http://panda.ispras.ru/~kuliamin/lectures-sdt/sdt-book-2006.pdf]

7) Амблер С. Гибкие технологии: экстремальное программирование и унифицированный процесс разработки - СПб.: Питер, 2005


Лекция 2. МОДЕЛИ И ИХ РОЛЬ В СОЗДАНИИ СИСТЕМ. ОБЪЕКТНАЯ МОДЕЛЬ

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

Сложность управления порождается следующими причинами:

Подход к решению этой проблемы основан на принципе «разделяй и властвуй» (divide et impera). Сложная программная система должна быть разделена на небольшие подсистемы, каждую из которых можно разрабатывать независимо (в какой-то степени) от других. Декомпозиция является главным способом преодоления сложности разработки ПО. Принципы декомпозиции:

При разбиении системы на подсистемы необходимо добиться выполнения следующих условий:

Следование этим правилам увеличивает понятность и модифицируемость создаваемого ПО.

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

В рамках обоих подходов используется понятие архитектуры ПО. Архитектура ПО - набор ключевых правил, определяющих организацию системы:

Архитектура ПО многомерна, поскольку различные специалисты работают с её различными аспектами. Различные представления архитектуры служат различным целям (модель «4+1»):

Среди 5-ти представлений особое место занимает представление вариантов использования, поскольку оно используется при управлении разработкой, служит своего рода скелетом проекта. В общем случае говорят о модели «N+1», имея в виду что перечень архитектурных представлений может варьироваться, но представление вариантов использования входит в него обязательно.

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

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

Гради Буч:

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

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

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

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

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

В процессе создания ПО используются следующие виды моделей:

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

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

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

Проблемы, стимулировавшие развитие ООП:

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

Краткая история ООП:

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

Объектная модель является естественным способом представления реального мира. Она является концептуальной основой ООП. Основными принципами ее построения являются:

Дополнительные принципы:

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

Инкапсуляция - локализация свойств и поведения в рамках единственной абстракции (рассматриваемой как «черный ящик»), скрывающей реализацию за общедоступным интерфейсом. При инкапсуляции отделяется внутреннее устройство объекта от его внешнего поведения. Объектный подход предполагает, что внутренние ресурсы объекта, скрыты от внешней среды. Абстрагирование и инкапсуляция являются взаимодополняющими принципами.

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

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

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

Типизация - способ защититься от использования объектов одного класса вместо другого, или, по крайней мере, управлять таким использованием.

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

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

Устойчивость - способность объекта сохранять свое существование во времени и/или пространстве (адресном, в частности при перемещении между узлами вычислительной системы). В частности, устойчивость объектов может быть обеспечена за счет их хранения в базе данных.

Переходим к основным понятиям объектно-ориентированного подхода (элементам объектной модели). К ним относятся: объект; класс; атрибут; операция; полиморфизм; наследование; компонент; пакет; подсистема; связь.

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

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

Атрибут - поименованное свойство класса, определяющее диапазон допустимых значений, которые могут принимать экземпляры данного свойства. Атрибуты могут быть скрыты от других классов, это определяет видимость атрибута: рublic (общий, открытый); private (закрытый, секретный); protected (защищенный). Мощность (кратность) атрибута показывает, сколько значений хранится в одном экземпляре атрибута. Если кратность больше 1, то атрибут описывает массив, список... Мощность указывается в профиле атрибута, примеры: name: string [1]; phones: string [*]. Описаны атрибуты: имя - строка; телефоны - список строк. По умолчанию кратность - 1.

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

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

Результат операции зависит от текущего состояния объекта. Виды операций:

Объект может быть абстракцией некоторой сущности предметной области (объект реального мира) или программной системы (архитектурный объект).


Сравнение архитектур традиционной и ОО-системы:

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

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

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

Компонент представляет собой физическую реализацию проектной абстракции и может быть: компонентом исходного кода (cpp-шник); компонентом времени выполнения (dll, ActiveX и т. п.); исполняемый компонентом (exe-шник). Компонент обеспечивает физическую реализацию набора интерфейсов. Компонентная разработка (component-based development) представляет собой создание программных систем, состоящих из компонентов (не путать с объектно-ориентированным программированием (ООП).

ООП - способ создания программных компонентов, базирующихся на объектах.

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

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

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

Между элементами объектной модели существуют различные виды связей.

Соединение (link) - физическая или концептуальная связь между объектами, позволяющая им взаимодействовать.


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

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


Агрегация- более сильный тип ассоциативной связи между целым и его частями (пример: автомобиль и мотор). Композиция - усиленная агрегация, когда часть не может существовать без целого (пример: университет, факультет, кафедра). Композиция и агрегация транзитивны, в том смысле, что если B является частью A, и C является частью B, то C также является частью A (но на диаграмме связи, возникающие за счет транзитивности, явно не изображаются).

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

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


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

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


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

Мощность Значение

1 Ровно один

0..* или * Ноль или больше

1..* Один или больше

0..1 Ноль или один

2..4 Заданный диапазон

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


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

Обратите внимание, как переместились мощности связей.


В UML 2.0 к объектной модели добавлено понятие N-арной ассоциации (для каждой комбинации объектов не более одной связи):

На рисунке представлена тернарная ассоциация и класс-ассоциация, связывающая проект, программистов, занятых в проекте, и языки, на которых они программируют в проекте. Некоторым аналогом тернарной ассоциации является реляционное отношение над тремя доменами (таблица со столбцами проект, программист, язык). Экземплярами N-арных ассоциаций являются N-арные соединения. Так, программистка Мэри в одном проекте пишет на Коболе, а в другом на Си. Заметим, что мощности на концах тернарной ассоциации не воспрещают Мэри писать в одном и том же проекте на разных языках (хотя на диаграмме объектов такое не показано). Единственное ограничение, наложенное в данном случае, - два разных тернарных соединения не могут связывать одну и ту же тройку объектов - например: Мэри, проект accountingSystem и Кобол.

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


В объектно-ориентированных языках N-арные ассоциации не поддерживаются

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

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

Полюса ассоциаций с мощностью «много» имеют еще две характеристики: упорядоченность связываемых объектов и повторяемость (т. е. образуют ли связываемые объекты множество или мультимножество). Различные сочетания этих характеристик образуют четыре типа полюсов: множества {set} (тип полюса по умолчанию), упорядоченные множества {ordered}, мультимножества {bar} и последовательности {sequence}:

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

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

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

Зависимость между пакетами возникает при импорте описаний элементов одного пакета в другой.

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

Общие атрибуты, операции и/или отношения отображаются на верхнем уровне иерархии. Заметим, что ассоциации класса-предка наследуются классами потомками, т. е. экземпляры потомков могут иметь соединения того же рода, что и экземпляры родительского класса. Действует принцип подстановки Лисковой (Liskov substitution principle - LSP), по которому любое утверждение, справедливое для экземпляров класса, сохраняет справедливость для экземпляров всех его подклассов. Например, из LSPследует, что экземпляр подкласса подкласса (класса «внука») может рассматриваться как экземпляр подкласса или экземпляр исходного класса (своего «деда»).

В объектной модели наследование может быть множественным. На связи наследования могут накладываться ограничения. Например, если необходимо, множественное наследование внутри некоторой иерархии классов может быть запрещено (над связью указывается ключевое слово: {disjoint}). Можно ограничить набор классов-наследников на некотором уровне иерархии наследования указав ограничение {comlpete}.

Обобщение рассматривается не только для классов, но и для ассоциаций. В этом случае стрелка обобщения соединяет две ассоциации. Заметим, что в моделях такое встречается редко. Ниже приведен пример, когда ассоциация между рейсом и самолетом переопределяется в классах наследниках ассоциациями-наследницами.


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

Литература к лекции 2


Лекция 3. Унифицированный язык моделирования (Unified Modelling Language)

Унифицированный язык моделирования UML (Unified Modeling Language) - это язык для определения, представления, проектирования и документирования программных систем, организационно-экономических систем, технических систем и других систем различной природы. UML содержит стандартный набор диаграмм и нотаций самых разнообразных видов.

UML является наследником методов объектно-ориентированного анализа и проектирования, появившихся в конце 1980-х и начале 1990-х годов. Создание UML началось в конце 1994 г., с объединения методов Booch и OMT (Object Modeling Technique) под эгидой компании Rational Software. К концу 1995 г. Гради Буч и Джеймс Рамбо создали первую спецификацию Unified Method, версия 0.8. Тогда же в 1995 г. к ним присоединился создатель метода OOSE (Object-Oriented Software Engineering) Ивар Якобсон. UML является унификацией методов Буча, Рамбо и Якобсона а также суммой передового опыта по разработке ПО:


Разработка UML преследовала следующие цели:

UML широко используется в индустрии ПО. Практически все мировые производители CASE-средств поддерживают UML в своих продуктах. В 1997 году Object Management Group (OMG) приняла стандарт UML 1.1. В 2004 году был пройден следующий важный этап - принят стандарт OMG UML версии 2.0. В настоящее время UML проходит процесс стандартизации ISO, статус международного стандарта пока имеет устаревшая версия UML 1.4 - ISO 19501. UML 2.1.2 является драфтом ISO DIS 19505-2. В марте 2011 года опубликована спецификация бета-версии UML 2.4.

Основные «строительные блоки» UML:

Состав диаграмм UML 1.х:

В UML 2.0 введены новые типы диаграмм, которых ранее не было: диаграммы обзора взаимодействия, временные диаграммы и диаграммы составных структур.


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

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

Диаграмма вариантов использования является самым общим представлением функциональных требований к системе. Детально функциональные требования описываются в документе, называемом «сценарий варианта использования» или «поток событий». Он подробно документирует процесса взаимодействия действующего лица с системой, реализуемого в рамках варианта использования.

В диаграммах вариантов использования может присутствовать несколько типов связей:

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


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

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


Каждое сообщение может быть описано в таком формате:

номер : переменная = операция (аргументы): возвращаемое значение

номер : - порядковый номер сообщения;

переменная = - указание, где будет сохранен результат;

операция (аргументы) - какая операция с какими аргументами будет вызвана;

возвращаемое значение - явное указание возвращаемого значения.


Любая из частей описания может отсутствовать.

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

В UML 2.0 диаграммы последовательности могут содержать блоки разных типов:


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

Вторым видом диаграмм взаимодействия являются коммуникационные диаграммы (в UML 1 их называли кооперативными). Как и диаграммы последовательности, они отображают поток событий варианта использования. На коммуникационных диаграммам внимание сконцентрировано на соединениях между объектами. Из них легче понять связи между объектами, однако, труднее уяснить последовательность событий. Объекты и/или действующие лица, обменивающиеся сообщениями, связываются линиями (соединениями), над которым в виде стрелок обозначаются сообщения. Нумерация сообщений указывает их последовательность во времени.



Рефлексивные сообщения (который объект посылает сам себе) изображаются над псевдосоединением - дугой над объектом.


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

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


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

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


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

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

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


События бывают разных видов: событие вызова (наступает, когда вызывается операция объекта, например, вклАвтопилот); событие изменения (наступает, когда становится истинным условие, например, when(NumUses=0), всегда начинается со слова when); событие времени (наступает, когда наступает заданный момент времени, или истекает заданный промежуток времени, например after5min, at 00-00). События, приписанные переходам, вызывают смену состояний. Если событие не отмечено на диаграмме, объект на него не реагирует. Если переходу не приписано событие, то это переход по завершению, который может осуществиться по окончании деятельности внутри события.


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

Когда объект находится в каком-то конкретном состоянии, могут выполняться различные процессы. Они, называются деятельностями состояния и указываются на диаграмме (см. состояние Превышение кредита, деятельность помечена do:). Деятельность состояния - это прерываемое поведение. Оно может выполняться до своего завершения, пока объект находится в данном состоянии, или может быть прервано переходом объекта в другое состояние. Деятельность состояния изображают внутри самого состояния, ей должно предшествовать слово do и двоеточие. Для состояния могут быть указаны входное и выходное действия. Входное действие выполняется всякий раз, когда объект переходит в данное состояние. В отличие от деятельности, входное действие рассматривается как непрерываемое. Входное действие также показывают внутри состояния, ему предшествует слово entry и двоеточие. Выходное действие осуществляется как составная часть любого выхода из состояния. Оно также является непрерываемым. Его изображают внутри состояния, ему предшествует слово exit и двоеточие. Действия, выполняемые в состоянии по наступлению события, помечаются словом именем события, после которого через двоеточие указывается действие. Это так называемые внутренние переходы. При выполнении внутреннего перехода действия по выходу и по входу не выполняются. Если вместо внутреннего перехода создать переход из состояния в само себя, то входное и выходное действия выполняются.

Переходом (transition) называется смена одного состояния объекта на другое. На диаграмме все переходы изображают в виде линий со стрелками. Объект может перейти в то же состояние, в котором он в настоящий момент находится. С переходом можно связать событие, сторожевое условие и действие. Событие вызывает переход из одного состояния в другое. Событие размещают на диаграмме вдоль линии перехода. Сторожевые условия определяют, когда переход может произойти, а когда нет. Их изображают на диаграмме вдоль линии перехода после имени события, заключая в квадратные скобки [ ]. Действие, являющееся частью перехода, изображают вдоль линии перехода после имени события и сторожевого условия, ему предшествует косая черта.


Пример перехода по событию вызова:

Когда объект находится в состоянии State1, и из очереди событий (а все они считаются упорядоченными и не совпадающими по времени) извлекается событие вызова операции Ev с аргументами Arg, то диаграмма предписывает сделать следующее:


Пример перехода по событию изменения:

Когда объект находится в состоянии State1, все время проверяется условие Guard. Заметьте, что это не сторожевое условие, это условие, являющееся частью события изменения. Если оно стало истинным, то диаграмма предписывает сделать следующее:


Пример перехода по завершению:

Когда объект находится в состоянии State1, и завершается выполнение деятельности ActDo1, диаграмма предписывает сделать следующее:


Внутри состояния можно отложить событие, для этого используется действие defer, например:

Пусть текущее состояние State1, очередь событий начинается с Ev1, Ev. Событие Ev1 откладывается. По событию Ev срабатывает переход в State2. Событие Ev1 в состоянии State2 не является отложенным, поэтому оно реактивируется (если отложено несколько событий, они активизируются в произвольном порядке). По событию Ev1 срабатывает переход в State1. Без defer событие Ev1 было бы извлечено из очереди и проигнорировано. Заметим, что если бы событие Ev было также отложенным в состоянии State1, то переход по Ev все равно произошел бы, без откладывания. Т. е. откладывать следует лишь те события, которые не приписаны переходам из данного состояния.

Для некоторых состояний (называемых суперсостояниями или композитными состояниями) указывают вложенные подсостояния. Внутри каждого такого состояния могут быть указано начальное и финальные состояния. Также в них может быть указано так называемое историческое состояние, переход в которое означает возврат к предыдущему активному подсостоянию, которое запоминается всякий раз при выходе из суперсостояния.Переход из исторического состояния является условным обозначением, это не переход как таковой, а указание на состояние, куда осуществляется переход в случае, если диаграмма предписывает перейти в историческое состояние, но предыстория пуста. В примере, переходя из состояния C в историческое состояние с пустой предысторией, автомат перейдёт в состояние A2. Если предыстория не пуста, автомат перейдёт либо в A1, либо в A2, в зависимости от предыстории, т. е. обстоятельств при которых возник переход по прерыванию. Выход из композитного состояния относится ко всем подсостояниям (в C можно перейти по событию interrupt из A1 или A2).

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


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

Переходы могут разделяться (см. ниже переход из S11) и сливаться (см переходы в S23). Переход из S11 в S12 осуществляется как обычно, когда активно S11. Переход в S23 осуществляется, когда одновременно активны S11 и S22.

Для сокращения количества переходов иногда применяют переходные псевдосостояния (черный кружок). На исходящих из него стрелках не могут быть отмечены события. В примере справа заданы 6 переходов. Каждый обозначается 2мя стрелками. Сторожевые условия получаются логическим умножением. То есть, имеем 3 перехода из State0 по событию e2:

в State2 со сторожевым условием [b<0 and a<0],

в State3 со сторожевым условием [b<0 and a=5],

в State4 со сторожевым условием [b<0 and a>7].

И 3 перехода из State1 по событию e1:

в State2 со сторожевым условием [b<0 and a<0],

в State3 со сторожевым условием [b<0 and a=5],

в State4 со сторожевым условием [b<0 and a>7].

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


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

На переходах из состояния выбора нельзя указывать состояния. Запрещается использовать сложные переходы с 2-мя и более состояниями выбора.

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


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

В UML 2 проведена граница между диаграммами состояний, базирующимися на формализме конечных автоматов, и диаграммами деятельности, основанными на сетях Петри. Основным элементом диаграмм деятельности является узел действия. Каждый такой узел представляет собой элементарную единицу работы (это может быть решение некоторой задачи, которую необходимо выполнить вручную или автоматизированным способом, или выполнение метода класса). Узел действия изображается в виде закругленного прямоугольника с текстовым описанием. Диаграмма деятельности может иметь входной узел, вообще говоря, не один, (в UML 1 должен быть ровно один), определяющий начало потока управления. Финальный узел необязателен. На диаграмме может быть несколько финальных узлов. На диаграмме могут присутствовать объекты и потоки объектов, в тех случаях, если объект используется или изменяется в одном из узлов действий. Поток объектов отмечается сплошной или пунктирной стрелкой от узла действия к объектному узлу или от объектного узла к узлу действия, использующего объект. Ребра (сплошные стрелки) между узлами действий показывают потоки управления или потоки объектов. Узел разветвления (узел принятия решения), а также узел объединения потоков изображается ромбом. Если необходимо показать, что две или более ветвей потока выполняются параллельно, используются «линейки синхронизации» - узлы разделения и узлы слияния (на рисунке - жирные горизонтальные линии).

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

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


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


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

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


Здесь прием возвращаемой позиции заказа на склад синхронизируется с изменением состояния объекта Item, который должен перейти в состояние returned (возвращен). Аналогично, возврат денег по возвращенному заказу синхронизируется со сменой состояния объекта Item на available (доступен). Обратите внимание, диаграмма с помощью вертикальных линий - «плавательных дорожек» разделяется на зоны ответственности (заказчик, продажи, бухгалтерия, склад).

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

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

• узел (node) - среда выполнения (вычислительный ресурс, изображаемый с закрашенными боковыми гранями) или устройство (дисковая память, контроллеры различных устройств и т.д.);

• соединение (connection) - канал взаимодействия узлов.

Для узла можно задать выполняющиеся на нем процессы.

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

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

Традиционные средства описания «линейных» языков (т. е. языков состоящих из цепочек символов) - БНФ, регулярные выражения, грамматики - плохо подходят для описания «плоского», т. е. двумерного, языка, каковым является UML. Поэтому для описания UML применяется UML. Описание строится в рамках объектной модели, называемой метамоделью UML. Метамодель UML определяет элементы языка, устанавливает их свойства, структуру и связи. Метамодели используются в разных языках, в OMGсоздано описание общей базы разных метамоделей - метаметамодель, называемое MOF (MetaObject Facility). Таким образом, выстроена иерархия моделей. На самом высоком уровне абстракции M3 находятся элементы MOF. Они являются классами метаклассов - элементов метамодели UML (уровень М2). Метаклассы являются классами элементов моделей систем, создаваемых при разработке ПО (уровень M1). Наконец, объекты, возникающие при запусках систем, образуют уровень M0 - уровень экземпляров времени исполнения. См. рис. на следующей странице.

Метамодель UML состоит из 3-х пакетов: Foundation, описывающего базовые понятия; Behavioral Elements, описывающего элементы для моделирования поведения; и Model Management, определяющего элементы управления моделями.

Элементы управления моделями в UML - это пакеты, подсистемы и модели, т. е. средства для группировки и выстраивания иерархий элементов моделей уровня M1.

Пакет Behavioral Elements состоит из подпакетов: Activity Graphs, описывающего элементы диаграмм деятельности; Collaborations описывающего понятие кооперации; Use Cases - с описаниями диаграмм вариантов использования; StateMachines - с описаниями диаграмм состояний; Common Behavior, определяющего общие понятия поведенческих


UML-моделей.

Пакет Foundation описывает базовые понятия UML. В нём три подпакета: Core, Data Types и Extension Mechamisms. Foundation::Data Types содержит базовые перечислимые типы, такие как перечисление видов ассоциаций (ассоциация, агрегация, композиция), перечисление направлений параметра операции (входной, выходной, смешанный, результат), перечисление областей действия атрибута (экземпляр или класс) и т. п.


Foundation::Core задаёт иерархию элементов UML:

Помимо связей наследования в Foundation::Core определяются структурные связи. Например, комментарий может быть присоединён к любому элементу, элемент-владелец может быть соединён с подчинёнными ему элементами, отношение - соединено со связываемыми им элементами и т. п. Подробнее с содержимым пакета можно ознакомиться в стандарте UML (OMG Unified Modeling Language Superstructure).

Комментарий (или примечание) - элемент UML, содержащий дополнительное текстовое пояснение. На диаграммах комментарий изображается в виде листа с загнутым уголком. Указание на связанный с комментарием элемент дается пунктирной линией.


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

Стереотип - это дополнительный тип элемента модели, который определяется на основе уже существующего элемента. Стереотипы расширяют нотацию модели. Любой элемент UML может быть расширен стереотипом. На диаграммах стереотип представляется в виде текстовой метки или пиктограммы (см. диаграмму классов выше). На диаграмме классам приписаны стереотипы: <<boundary>> (граничный класс), <<entity>> (класс-сущность) и <<control>> (управляющий класс). Тем самым элемент UML отображает не только класс, но и его ответственность, т. е. роль, которую он играет в системе.

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


Именованные значения - дополнительные значения, приписываемые элементам UML с указанием имени и самого значения. В качестве имён могут использоваться предопределённые термины, такие как disjoint, complete, incomplete, subset и др. Перечисленные именованные значения обычно указываются только именем (предполагаемое по умолчанию значение - true). Примеры полного указания именованных значений: version=1.2.3 или location=server. Для стереотипа можно задавать связанный с ним набор именованных значений, играющих в таком случае роли его атрибутов.

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

Подробнее ограничения рассмотрим на следующей лекции.

Литература к лекции 3

Ссылки:

http://www.uml.org

http://www.uml2.ru


Лекция 4. Объектный язык ограничений (OCL)

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


Рассмотрим пример:

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

Ограничение, записанное на естественном языке, неформально, его можно неправильно трактовать (например, что чартерные рейсы должны выполняться старыми самолетами, а регулярные - новыми). Поэтому имеет смысл использовать для записи формальный язык, который не допускает произвольных толкований и имеет стандартный синтаксис и семантику. Таковым является объектный язык ограничений OCL (Object Constraint Language), являющийся одним из расширений UML. С использованием OCL ограничение может быть записано так:

contextРейс

inv: тип=Тип::грузовой implies Самолет.тип= Тип::грузовой

inv: тип=Тип::пассажирский impliesСамолет.тип= Тип::пассажирский

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

Классификация ограничений:

В примере мы описали инварианты класса Рейс.

Характеристики OCL:

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

Синтаксис OCL-выражения

<OCL-выражение> ::=

<указание контекста>

[(inv | pre | post | body | init | derive | def) : <тело выражения>]

В записи использованы символы языка БНФ: <> выделяют нетерминалы, ( | ) -вхождение одной из указанных альтернатив, [] вхождение 1 или более раз, {} - вхождение 0 или более раз. Терминалы записаны жирным шрифтом.

Контекст. В любом OCL-выражении указывается определенный контекст. Как правило, контекстом является элемент модели (пакет, класс, атрибут, операция), с которым связано ограничение.

<указание контекста> ::= context <имя элемента модели>

В примере контекстом является класс Рейс.

Для того чтобы сослаться на контекст в теле выражения используется слово self. Чтобы много раз не писать self, оно часто опускается. По смыслу self аналогично this в C++. В примере тип - сокращенная запись self.тип.

В теле выражения используются

Логический тип OCL почти таков как в языках программирования. Есть дополнительные операции xor и implies. Приоритет логических операций (кроме not) меньше арифметических и операций сравнения - так проще записывать сложные логические выражения. Целый и вещественный типы также стандартны. Имеются дополнительные операции a.max(b) и a.min(b), возвращающие максимум и минимум из двух чисел. Строки также похожи на строки языков программирования, только их нельзя сравнивать в лексикографическом порядке.

Примеры использования простых типов:

context Airline inv: name.toLowerCase() = 'klm' - здесь 'klm' - строка, toLowerCase() - стандартная операция над строкой, дающая как результат строку в нижнем регистре. Смысл ограничения: у любого экземпляра класса Airline значение атрибута name, записанное строчным буквами совпадает со строкой 'klm'.

context Passenger inv: age >= ((9.6 - 3.5)* 3.1).floor() implies mature = true - здесь floor() - «округление вниз». Смысл ограничения: у любого экземпляра класса Passenger значения атрибутов age и mature таковы, что если age > 18, то mature = true.


Ограничения могут быть указаны на диаграмме в примечании, якорь примечания в таком случае играет роль указателя на контекст.. Например, ограничение context Flight inv: self.duration < 4 может изображаться на диаграмме так:

В OCL употребляются условные выражения:

<условное выражение> ::=

if <логическое выражение> then <выражение>

else <выражение>

endif

Пример: if (x >= 0) then x else - x endif возвращает модуль x или x.abs().

В телах OCL-выражений используются типы и имена (классов, атрибутов, операций) из модели. Далее в примерах мы будем рассматривать модель авиаперевозок:


Обратите внимание на производные атрибуты в классе Flight (arrivalTime, nrPassengers) и статический атрибут minAge класса Passenger.

Для указания атрибута или операции используется выражение с точкой:

<выражение>.<имя>

context Flight inv: self.maxNrPassengers <= 1000 - в любом рейсе максимальное количество пассажиров не превышает 1000 (использован атрибут объекта - экземпляра класса Flight).

context Flight:maxNrPassengers:Integer init: 1000 - в любом рейсе максимальное количество пассажиров по умолчанию = 1000.

context Passenger inv: self.age >= Passenger::minAge- у любого пассажира возраст больше минимального (использован атрибут age экземпляра класса Passenger и атрибут того же класса minAge).

Примеры с производными атрибутами:

context Flight def: arrivalTime:Time = departTime.plus(duration)

или

context Flight:arrivalTime derive: departTime.plus(duration)

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

Пример определения операции:

context Interval::equals(i:Interval):Boolean body:

(self.nrOfDays*60+self.nrOfHours)*60+self.nrOfMins =

(i.nrOfDays*60+i.nrOfHours)*60+i.nrOfMins

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

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

context Flight

inv: self.origin <> self.destination

inv: self.origin.name = 'Amsterdam' - у любого рейса аэропорт назначения и аэропорт вылета не совпадают, а также аэропорт вылета называется 'Amsterdam'.

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


context Person inv:

if employer.name = 'OAO MMM' then

Job.type = #trainer

else

Job.type = #programmer

endif

Смысл ограничения: у любой персоны, занятой в 'OAO MMM', тип работы trainer, у остальных тип работы programmer. То же самое по смыслу ограничение можно записать, используя в качестве контекста класс ассоциаций:

context Job inv:

if employer.name = 'OAO MMM' then

self.type = #trainer

else

self.type = #programmer

endif

При навигации по связям, если на другом конце указана мощность связи *, от одного объекта мы приходим к нескольким связанным с ним (например, один аэропорт является аэропортом вылета для нескольких рейсов), поэтому в OCL введено понятие коллекции. Вообще говоря, коллекции могут состоять либо из объектов, либо из элементов простых типов, либо из элементов типов, определенных в модели, либо из коллекций. Виды коллекций:

В OCL имеется большое количество предопределенных операций над коллекциями (isEmpty, size, includes, union …). Синтаксис: <коллекция> -> <операция>

Операция collect() возвращает коллекцию значений, полученных при вычислениях выражения для всех элементов коллекции. Запись: <коллекция>->collect(<выражение>) - здесь и далее круглые скобки и | - символы OCL, а не языка БНФ. Сокращенная запись: <коллекция>.<выражение>

Пример: context Airport inv: self.arrivingFlights-> collect(airLine) -> notEmpty() - для любого аэропорта множество авиакомпаний, выполняющих прибывающие рейсы, не пусто. Приведена сокращенная запись. Полная (только правая часть):

->collect(e:Flight| e.AirLine)->notEmpty()

Операция select() возвращает совокупность тех элементов коллекции, для которых <выражение> истинно. Запись: <коллекция>->select(<выражение>)

Пример: context Airport inv: self.departingFlights->select(duration<4)->notEmpty() - для любого аэропорта есть хоть один отправляющийся рейс длительностью менее 4 часов. Приведена сокращенная запись. Полная (только правая часть):

->select(e:Flight| e.duration < 4)->notEmpty()

Операция forAll() возвращает true если <выражение> истинно для всех элементов коллекции, в остальных случаях возвращается false. Запись:

<коллекция>->forAll(<выражение>)

Пример: context Airport inv: self.departingFlights->forAll(maxNrPassengers< 1000) - для любого аэропорта справедливо, что у любого отправляющегося рейса максимальное количество пассажиров < 1000. Приведена сокращенная запись. Полная (только правая часть): ->forAll(e:Flight| e.maxNrPassengers< 1000)

Другой пример, когда внутри forAll производится проверка условия относительно всех пар объектов коллекции:

context AirLine inv:

AirLine.allInstances()->forAll( e1, e2 : AirLine | e1 <> e2 implies e1.name <> e2.name)

Ограничение указывает, что для любых двух разных авиакомпаний имена не совпадают. Операция allInstances() может применяться к любому классу (но не объекту! self.allInstances() - ошибка), чтобы получить коллекцию всех экземпляров класса.

Можно задать то же самое ограничение с помощью вложенного forAll:

context AirLine inv: AirLine.allInstances() ->forAll(e1:AirLine | AirLine.allInstances()

->forAll(e2: AirLine | e1 <> e2 implies e1.name <> e2.name))

Операция exists() возвращает true если хотя бы для одного элемента коллекции <выражение> истинно, в остальных случаях возвращается false. Запись:

<коллекция>->exists(<выражение>)

context Airport inv:

self.departingFlights->exists(departTime.hour<6)

Приведена сокращенная запись. Полная (только правая часть):

->exists(e:Flight| e. < departTime.hour<6)

Другие операции над коллекциями:

Операции для упорядоченных коллекций:

Коллекции можно сравнивать на = и <>. Преобразование коллекций к другому виду осуществляется при помощи операций asSet(), asBag(), asOrderedSet(), asSequence(). Преобразование типов осуществляется с помощью операции oclAsType(type). Коллекцию коллекций можно сделать «плоской» с помощью операции flatten(). Примеры ее работы:

Set { Set { 1, 2 }, Set { 2, 3 }, Set { 4, 5, 6 } } à flatten à Set { 1, 2, 3, 4, 5, 6 }

Bag { Set { 1, 2 }, Set { 1, 2 }, Set { 4, 5, 6 } } àflatten à Bag { 1, 1, 2, 2, 4, 5, 6 }

Sequence { Set { 1, 2 }, Set { 2, 3 }, Set { 4, 5, 6 } }àflatten àSequence { 2, 1, 2, 3, 5, 6, 4 }

Операция closure(), примененная к коллекции вычисляет замыкание. Например:

context Person::posterity: Set( Person ) def:

self->AsSet()->closure( children )

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

context Person:: forefathers: Set( Person ) def:

self->AsSet()->closure( parents )

Дополнительные возможности OCL:

С помощью result в постусловии можно указать, что операция возвращает в качестве результата:

context Airline::servedAirports() : Set(Airport)

pre : --none

post: result = flights.destination->asSet

@pre в постусловии дает возможность использовать значения атрибутов, какими они были в начале выполнения операции

context Passenger::Book(f:Flight)

pre : Flight.nrPassengers < Flight.maxNrPassengers

post: Flight.nrPassengers = Flight.nrPassengers@pre + 1

Конструкция let определяет локальную переменную. Запись:

let <name> : <type> = <expression1> in <expression2>

Пример: context Airport inv: let supportedAirlines : Set(Airline) = self.arrivingFlights ->collect(airLine) in (supportedAirlines ->notEmpty) and (supportedAirlines ->size < 500) - здесь для упрощения логического выражения определена локальная переменная supportedAirlines, представляющая собой коллекцию авиалиний, обслуживающих, прибывающие в аэропорт рейсы. Указано, что для любого аэропорта таких авиалиний должно быть больше нуля, но меньше 500.

Конструкция iterate позволяет описывать нестандартные операции над коллекциями. Запись: <коллекция>->

iterate(<переменная1> : <тип>; <переменная2> : <тип> [= <нач. значение>] | <тело>)

где <переменная1> - параметр итератора, <переменная2> - результат итератора, <тело> - OCL-выражение с <переменная1> и <переменная2>.

Например, ограничение:

context Airline inv: self.flights->select(maxNrPassengers > 150)->notEmpty

идентично:

context Airline inv: self.flights->iterate (f : Flight; answer : Set(Flight) = Set{ } |

if f.maxNrPassengers > 150 then answer->including(f) else answer endif )->notEmpty

Поясним второе OCL-выражение. Для авиалинии будет собрана коллекция всех ее рейсов и на этой коллекции будет запущен итератор. В начале работы результат итератора инициализируется пустым множеством. Затем для каждого рейса f из коллекции, одного за другим, будет вычислено условное выражение, которое добавит в результат лишь те рейсы, maxNrPassengers которых больше 150.

N-ки - это составные значения, содержащие несколько более простых значений. Например:

Tuple{name: String= 'John,' age: Integer= 10} - n-ка из двух значений: строки по имени name и целого по имени age. Для выделения одного значения в составе n-ки используется точка и имя значения:

Tuple {a: Collection(Integer) = Set{1, 3, 4}, b: String = 'foo,'}.a = Set{1, 3, 4}

Порядок записи значений в составе n-ки не играет ни какой роли, т. е. выполняется тождество:

Tuple {name = 'John,' age = 10} = Tuple {age = 10, name = 'John'}

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

context Person def:

attr statistics : Set(Tuple(company: Company, numEmployees: Integer, totalSalary: Integer)) =

Companies->collect(c | Tuple { company: Company = c,

numEmployees: Integer = c.employee->size(),

totalSalary: Integer = c.Job.salary->sum()}

Обратите внимание на конструкцию Tuple(…), которая использована для конструирования типа n-ок в возвращаемой коллекции. Значения n-ок описываются конструкцией Tuple{…}.

Есть стандартная операция с коллекциями, возвращающая коллекцию n-ок - декартово произведение: product(coll). Ниже дано её явное определение с помощью итератора:

self->iterate (e1; acc: Set(Tuple(first: T, second: T2)) = Set{} |

c2->iterate (e2; acc2: Set(Tuple(first: T, second: T2))

= acc | acc2->including (Tuple{first = e1, second = e2}) ) )

Пример: Set {1, 2}produce(Set{'a', 'b', 'c'}) = Set{Tuple{first= 1, second='a'}, Tuple{first= 1, second='b'}, Tuple{first= 1, second='c'}, Tuple{first= 2, second='a'}, Tuple{first= 2, second='b'}, Tuple{first= 2, second='c'}}.

Операция oclInState() возвращает истину, если объект находится в определенном состоянии. Пример:

context Bottle inv:

self.oclInState(closed) implies filled = #full

Ограничение описывает, что все закрытые бутылки должны быть полны (состояние открытых не определяется).

При наследовании ограничений работает принцип подстановки Лисковой (Liskov's Substitution Principle): «Где может находиться экземпляр суперкласса, туда всегда может быть подставлен экземпляр его любого подкласса.» Это означает, что:

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

context ГрузовойСамолет inv:

Рейс->forAll(r | r.oclIsTypeOf(ГрузовойРейс))

context ПассажирскийСамолет inv:

Рейс->forAll(r | r.oclIsTypeOf(ПассажирскийРейс))

Если необходимо определить может ли объект рассматриваться как экземпляр какого-либо класса (например, объект подкласса - как экземпляр суперкласса), используется операция OclIsKindOf(Type). Так для объекта класса Пассажирский самолет истина будет получена если Type=Самолет или Type=Пассажирский самолет, в то время как выражение ocllsTypeOf(Type) истинно, только если Type - непосредственный тип объекта, т. е. Type=Пассажирский самолет. Для приведения типов используется операция OclAsType(<тип>).

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

Подведем итоги.

Литература к лекции 4


Лекция 5. Моделирование бизнес-процессов

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

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

Бизнес-модель - это формализованное описание бизнес-процессов предприятия, фиксирующее существующее положение дел (модель AS-IS «как есть») или устанавливающее новые усовершенствованные способы осуществления деятельности (модель AS-TO-BE «как будет»). Цели бизнес-моделирования:


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

Бизнес-модель должна давать ответы на вопросы:

Рассмотрим методику моделирования деловых процессов, являющуюся составной частью технологии Rational Unified Process.

Аналитик бизнес-процессов возглавляет и координирует бизнес-моделирование. Он отвечает за:

Модель бизнес-процессов (Business Use Case Model) - модель, описывающая бизнес-процессы организации в терминах ролей и их потребностей. Она похожа на модель вариантов использования UML, но в ней используются деловые стереотипы Business Actor (деловое действующее лицо) и Business Use Case (бизнес-процесс - стереотип варианта использования). Из этой модели видно, в каком контексте работает предприятие, но не видно как именно протекает его работа (это описывает модель бизнес-анализа).

Деловое действующее лицо (business actor) - некоторая роль, выполняемая частью окружения организации по отношению к её бизнес-процессам. Кандидатами в деловые действующие лица являются: акционеры, заказчики, поставщики, партнеры, потенциальные клиенты, местные органы власти, коллеги из подразделений, не охваченных моделью, внешние бизнес-системы (предприятия или подразделения). Деловыми действующими лицами, как правило, не являются должностные лица, работающие на предприятии. Обнаружить действующих лиц бизнес-процессов можно, найдя ответы на вопросы:

Бизнес процесс (Business use-case) описывает последовательность действий в рамках экономической деятельности предприятия, приносящую ощутимый результат конкретному деловому действующему лицу.


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

На диаграмме стереотипы изображены пиктограммами. Деловое действующее лицо изображается как действующее лицо, но добавлен штрих («гвоздь»), подчеркивающий, что это элемент бизнес-модели. Аналогично пиктограмма бизнес-процесса строится из изображения варианта использования добавлением штриха. Согласно приведённой диаграмме определены два бизнес-процесса и два деловых лица. Основным деловым действующим лицом бизнес-процесса «Пройти регистрацию» является Пассажир. В ходе этого бизнес-процесса достигается цель этого лица. Основным деловым действующим лицом бизнес-процесса «Зарегистрировать группу» является Руководитель туристической группы. Во втором случае пассажир - вспомогательное деловое лицо.

Каждый бизнес-процесс сопровождается спецификацией, в которой содержится:

Пример:

  1. Пассажир встает в очередь к стойке регистратора.
  2. Пассажир предъявляет билет регистратору.
  3. Регистратор подтверждает правильность билета.
  4. Регистратор оформляет багаж.
  5. Регистратор резервирует место для пассажира.
  6. Регистратор печатает посадочный талон.
  7. Регистратор выдает пассажиру посадочный талон и квитанцию на багаж.
  8. Пассажир принимает талон и квитанцию и уходит от стойки регистратора.
  9. Деловой процесс заканчивается успешно.
Альтернативные сценарии:

3а. Билет неправильно оформлен.

3a.1. Регистратор отсылает пассажира к агенту по перевозкам. Бизнес-процесс заканчивается неудачей.

4а. Багаж превышает установленный вес.

4a.1. Регистратор рассчитывает и оформляет доплату.

4a.2. Пассажир осуществляет доплату.

4a.3. Деловой процесс продолжается с шага 5 основного сценария.

Специальные требования - Время регистрации не должно превышать 1 минуты.

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

Модель бизнес-анализа (модель бизнес-объектов) создается другим исполнителем в рамках RUP - бизнес-разработчиком, но руководит её созданием бизнес-аналитик.

Бизнес-разработчик выполняет следующие деятельности:


Рис. Деятельности, выполняемые бизнес-разработчиком и рабочие документы, создаваемые им.

Модель бизнес-анализа (Business Analysis Model)- это объектная модель, элементами которой являются исполнитель (business worker) и бизнес-сущность (business entity). Эта модель описывает внутреннее устройство бизнес-процессов с точки зрения структуры и поведения. Но из этой модели нельзя понять деловое окружение предприятия (что описано моделью бизнес-процессов).

Business worker - исполнитель, действующий в рамках предприятия. В отличие от делового действующего лица исполнитель работает в организации, имеет должность. Он связан ассоциациями с другими исполнителями и бизнес-сущностями, которыми может манипулировать, как предписано сценариями бизнес-процессов. Представляется на диаграммах как класс со стереотипом «business worker» или пиктограммой - кружок со стрелкой, действующим лицом внутри и «штрихом-гвоздём».

Деловая сущность (Business entity) - это ресурс (информационный, материальный, финансовый и т. д.), не инициирующий никаких взаимодействий, он может участвовать в реализациях различных бизнес-процессов и является предметом различных манипуляций со стороны исполнителей. На диаграммах представлен классом со стереотипом «business entity» или пиктограммой - кружок над чертой со штрихом-«гвоздём».

Модель бизнес-анализа включает в себя диаграммы разных видов:

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

Бизнес-разработчик должен учитывать все бизнес-правила и отслеживать их выполнение в модели бизнес-анализа.

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

Реализация бизнес-процесса - кооперация со стереотипом «business use case realization»). Она описывает структуру бизнес-классов (исполнителей и деловых сущностей) и взаимодействие их экземпляров (бизнес-объектов) при реализации конкретного бизнес-процесса. Другими словами, диаграммы классов, диаграммы взаимодействия, относящиеся к одному бизнес-процессу объединяются в одну реализацию бизнес-процесса.

Бизнес-система - пакет со стереотипом «business system» - объединяет относящихся к одному подразделению организации исполнителей и экономические ресурсы (деловые сущности), относящиеся к ведению подразделения, а также связанные с ними диаграммы состояний. Если какая-либо реализация бизнес-процесса осуществляется целиком в рамках подразделения, в соответствующую бизнес-систему помещается реализация этого бизнес-процесса (кооперация). Большая бизнес-система может быть разделена на части - бизнес-системы подчиненных отделов подразделения.

Ход бизнес-моделирования в целом отображает следующая диаграмма деятельности:

При оценивании бизнеса создаются следующие документы:

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

Литература к лекции 5


Лекция 6. ОПРЕДЕЛЕНИЕ ТРЕБОВАНИЙ К ПРОГРАММНОМУ ОБЕСПЕЧЕНИЮ

Требование - это условие, которому должно удовлетворять программное обеспечение, или свойство, которым оно должно обладать, чтобы:

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

Методы выявления требований:

В ходе проекта работа с требованиями делится на следующие этапы:

Любое требование в базе проекта имеет следующие атрибуты:

Хороший набор требований удовлетворяет следующим показателям качества (IEEE 830-1998 «Recommended Practice for Software Requirements Specifications»):

Выявленные требования к ПО оформляются в виде ряда документов и моделей. К основным документам, регламентируемым технологией Rational Unified Process, относятся:

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

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

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

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

Существуют четыре уровня точности описания вариантов использования, расположенные по степени повышения точности:

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

• существенное влияние на архитектуру системы;

• рискованные, сложные для реализации или срочные функции;

• применение новой, неапробированной технологии;

• значимость в экономических процессах.

Потоки событий вариантов использования представляют собой перенумерованные наборы шагов. Используются шаги трёх типов: действие системы (например, «Система запрашивает имя пользователя и пароль»); реакция действующего лица («Пользователь вводит имя и пароль»); управление потоком («Выполнение переходит на начало основного потока«). Структура предложений, описывающих шаги, одинакова: подлежащее, сказуемое, остальные части речи. От неё отходят лишь при описании циклов и ветвлений. Цикл задается составным описанием, в начале которого указывается условие цикла («Для каждого ... выполняется») или количество повторений. Далее следует тело цикла -- последовательность вложенных шагов (см. шаг 4 основного потока варианта использования «Ввести заказ»). Ветвление в тривиальных случаях, когда альтернативная ветвь пуста, допускается описывать предложением с союзом если («...»). Чаще ветвление описывают с помощью альтернативных потоков. В основном потоке варианта использования «Ввести заказ» 9-ый шаг указывает основное продолжение потока, а альтернативный поток «9А. Бухгалтерская система временно недоступна» содержит второй вариант развития событий. Как правило, действия по проверке условия ветвления не описывают. Вместо этого указывается шаг, на котором система (или действующее лицо) подтверждает, что условие выполнено, в основном потоке, или обнаруживает, что условие нарушено, в альтернативном потоке.

Пример описания варианта использования (в нем приводятся не все рекомендованные Коберном пункты, область действия описания - система как «черный ящик»):

Вариант использования «Ввести заказ»

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

Основной поток событий

1. Продавец обращается к системе, чтобы ввести новый заказ.

2. Система запрашивает данные о заказе.

3. Продавец вводит данные о заказчике и дате поставки заказа.

4. Для каждой позиции нового заказа выполняется:

4.1. Продавец вводит наименование и количество.

4.2. Система подтверждает, что наименование и количество указаны верно.

5. Продавец сообщает системе о необходимости сохранить заказ.

6. Система сохраняет данные о заказе.

7. Система запрашивает сеанс связи с бухгалтерской системой.

9. Бухгалтерская система подтверждает готовность к приёму данных о заказе.

10. Система передает данные о заказе и завершает сеанс.

11. Бухгалтерская система подтверждает получение данных о заказе.

Альтернативные потоки

4.2А. Ошибка при вводе позиции заказа

1. Система обнаруживает, что наименование предмета мебели либо количество указаны неверно.

2. Система выдает сообщение об ошибке.

3. Управление передается на шаг 4 основного потока.

9А. Бухгалтерская система временно недоступна

9А.1. Система обнаруживает, что невозможно установить связь с бухгалтерской системой.

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

9А.3. Система ожидает некоторое установленное время.

9А.4. Выполнение передается на шаг 7 основного потока.

9А.2A. Бухгалтерская система постоянно недоступна

9А.2A.1. Система обнаруживает, что исчерпан лимит попыток для установки связи с бухгалтерской системой.

9А.2A.2. Система удаляет сохранённые сведения о заказе.

9А.2A.3. Система выдает сообщение об ошибке.

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

Постусловия: Если вариант использования завершится успешно, данные о заказе будут внесены в систему обработки заказов и переданы в бухгалтерскую систему. В противном случае система данные о заказе не сохраняет и выдаёт сообщение об ошибке.

При определении требований к системе рекомендуется рассматривать систему как «черный ящик». Следует придерживаться правил:

Связи коммуникации (ассоциации) между вариантами использования и действующими лицами отображаются на диаграмме вариантов использования:


Правила составления этих диаграмм:

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


Инструментом структуризации также является обобщение вариантов использования.


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

Методика моделирования вариантов использования в технологии Rational Unified Process предусматривает специальное соглашение, связанное с группировкой структурных элементов и диаграмм модели. Это соглашение включает следующие правила:

• Все действующие лица, варианты использования и диаграммы вариантов использования помещаются в пакет с именем Use Case Model.

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

Дисциплина определения требований в рамках RUP описывается следующим набором ролей и деятельностей:


Наборы рабочих продуктов определения требований:


Спецификация требований в технологии Rational Unified Process не требует обязательного моделирования бизнес-процессов организации, для которых создается ПО, однако, наличие бизнес-моделей существенно упрощает построение системной модели вариантов использования. При переходе от бизнес-модели к начальной версии модели вариантов использования применяются следующие правила:

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


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


Такая начальная версия модели описывает минимальный вариант системы, пользователями которой являются только исполнители бизнес-процессов. Если в дальнейшем, в процессе развития системы, ее непосредственными пользователями будут становиться действующие лица бизнес-процессов, то модель вариантов использования будет соответствующим образом модифицироваться.

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


Пример диаграммы деятельности для потоков событий одного варианта использования:

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

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


Лекция 7. Анализ и проектирование программного обеспечения. Анализ ПО

Сначала охарактеризуем в целом технологический процесс анализа и проектирования (один из процессов в рамках RUP). Его цели:

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


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

Эскизная архитектура включает:

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

Определяются: проектные классы; механизмы проектирования; представление размещения.

Анализ поведения включает:

Проектирование элементов включает:

Проектирование БД включает:

Анализ и проектирование отличаются подходом к создаваемой системе. Анализ характеризуется тем, что:

Характеристики проектирования:

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

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

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

Обязанности архитектора во время анализа:

Обязанности разработчика во время анализа:

Специализированные разработчики (разработчик БД, разработчик элементов реального времени) подключаются при проектировании.

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

Рецензент оценивает решения, принятые в ходе процесса и созданные рабочие продукты (артефакты, т. е. документы, модели и т. д.).

Анализ включает два вида деятельности выполняемые друг за другом:

Архитектурный анализ выполняется архитектором системы и включает в себя следующие работы:

Соглашения моделирования определяют:

Соглашения фиксируются в документе «Руководящие указания по проектированию» (Design Guidelines). Пример соглашений:

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

Примеры механизмов анализа:


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

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

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

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

Архитектурный образец «Layers»:

Прикладной уровень (Application subsystems) - реализация функциональности вариантов использования.

Бизнес-уровень (Business-specific) - набор компонентов, специфичных для конкретной предметной области.

Уровень промежуточного ПО (Middleware) - платформо-независимые сервисы (GUI, ORB, …)

Уровень базового ПО (System software) - обеспечение вычислительной и сетевой инфраструктуры (ОС, сетевые протоколы и др.).

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


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

Образец«Model-view-controller» родом из языка Smalltalk. Проблема, которую он решает, формулируется так: Изменения во внешнем представлении достаточно вероятны, одна и та же информация представляется по-разному в нескольких местах, система должна быстро реагировать на изменения данных.

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

Рис. Один из вариантов организации взаимодействия, предписываемый образцом MVC.

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

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

Анализ вариантов использования выполняется разработчиками и включает в себя:

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

Шаги анализа вариантов использования:

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

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


Правило выявления управляющих классов: для каждого варианта использования создается ответственный за его реализацию класс управления.

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

Все созданные при анализе данного варианта использования классы анализа помещаются на диаграмму VOPC (View Of Participating Classes). Пример см. на стр. 3.


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

Виды обязанностей классов:

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

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

При построении диаграмм взаимодействия возникают проблемы правильного распределения обязанностей между классами. Для их решения существует ряд образцов: Information Expert, Creator, Low Coupling, High Cohesion и др.

Образец "Information Expert". Проблема: Нужно определить наиболее общий принцип распределения обязанностей между классами. Решение: Следует назначить обязанность информационному эксперту - классу, у которого имеется информация, требуемая для выполнения обязанности. Пример:

При выполнении подчиненного потока событий "Обновить график" варианта использования "Зарегистрироваться на курсы" студент-пользователь должен получить доступ к своему графику прежде, чем изменить его. Согласно образцу "Information Expert", нужно определить, объект какого класса содержит информацию, необходимую для доступа к графику. На эту роль информационного эксперта, очевидно, претендует объект класса-сущности Student, поскольку график принадлежит именно ему. Поэтому сообщение 3 "get schedule(forSemester)" должно быть направлено от контроллера объекту класса Student. После того, как студент получит график и внесет в него необходимые изменения, они должны быть зафиксированы в объекте Schedule. В данном случае уже сам объекте Schedule будет играть роль информационного эксперта, поскольку он непосредственно доступен контроллеру, и сообщение 10 "update with new selections" будет направлено именно ему.

Следствия:

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

В некоторых ситуациях применение образца Information Expert нежелательно.

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

• класс В агрегирует, содержит или активно использует объекты класса А;

• класс В обладает данными инициализации, которые будут передаваться объектам класса А при их создании (т.е. класс В является информационным экспертом).

Класс В при этом определяется как создатель (creator) объектов класса А.

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

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

Согласно образцу "Creator", оба решения подходят, так как с одной стороны объект-контроллер обладает данными, необходимыми для инициализации, с другой стороны объект Student агрегирует объекты Schedule.

Следствия: Образец "Creator" определяет способ распределения обязанностей, связанный с процессом создания объектов. В объектно-ориентированных системах эта задача является наиболее распространенной. Основным назначением образца Creator является выявление объекта-создателя, который при возникновении любого события должен быть связан со всеми созданными им объектами. При таком подходе обеспечивается низкая степень связанности объектов.

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

Образец "Low Coupling" (низкая связанность). Проблема: Нужно распределить обязанности между классами таким образом, чтобы снизить взаимное влияние изменений в них и повысить возможность повторного использования. Решение: Следует распределить обязанности таким образом, чтобы обеспечить низкую связанность. Связанность (coupling) - это мера, определяющая, насколько жестко один элемент связан с другими элементами, или каким количеством данных о других элементах он обладает. Элемент с низкой связанностью зависит от небольшого числа других элементов. Класс с высокой связанностью зависит множества других классов. Наличие таких классов нежелательно, поскольку оно приводит к возникновению следующих проблем:

Пример: Рассмотрим подчиненный поток событий "Создать график" варианта использования "Зарегистрироваться на курсы" (предыдущий рисунок). Согласно образцу "Low Coupling", наилучшим решением является вариант справа, поскольку при этом у класса RegistrationController будет на одну связь меньше (т.е., будет обеспечена более низкая связанность).

Следствия: Образец Low Coupling поддерживает независимость классов, что, в свою очередь, повышает возможности повторного использования. Его нельзя рассматривать изолированно от других образцов, таких как Information Expert и High Cohesion. Он также обеспечивает выполнение одного из основных принципов проектирования, применяемых при распределении обязанностей.

Образец "High Cohesion" (высокая функциональная прочность или сильное зацепление обязанностей). Проблема: Нужно распределить обязанности между классами таким образом, чтобы каждый класс не выполнял много разнородных функций или несвязанных между собой обязанностей. Такие классы создавать нежелательно, поскольку они приводят к возникновению таких же проблем, как у классов с сильной связанностью. Решение: Следует распределить обязанности таким образом, чтобы обеспечить высокую функциональную прочность. В терминах объектно-ориентированного проектирования функциональная прочность (cohesion) - это мера взаимосвязи и непротиворечивости обязанностей класса. Считается, что элемент обладает высокой прочностью, если его обязанности тесно связаны между собой, и он не выполняет излишнего объема работы. В роли таких элементов могут выступать классы, подсистемы, модули и т.д.

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

Пример: Используем тот же пример, что и для предыдущего образца. Согласно образцу "High Cohesion", наилучшим решением также является вариант справа, поскольку при этом класс RegistrationController делегирует обязанность создания нового объекта класса Schedule классу Student, и у самого класса RegistrationController будет на одну обязанность меньше (т.е., его прочность будет выше). Обязанность, отданная Student, относится к тому же роду, что и другие его обязанности, так как касается порождения объектов - частей.

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

Следует обратить внимание, что обязанностью экземпляров классов является не только прием и обработка сообщений, но и их отправка другим объектам. То есть объект обязан знать о других объектах, которым он должен будет посылать сообщения. Распределить обязанности такого рода позволяют образцы Сценарий транзакции и Модель предметной области. Они введены Мартином Фаулером в книге «Архитектура корпоративных программных приложений».

Образец «Сценарий транзакции»

Аннотация: В управляющем классе заводится по одной операции на каждый запрос. Экземпляр управляющего класса обеспечивает правильную последовательность шагов сценария обработки каждого запроса, рассылая сообщения объектам сущностям, которые сами по себе не реализуют сложного поведения. Типовая последовательность шагов такова: 1) прием входного запроса; 2) получение данных из базы; 3) обработка данных; 4) выдача результата. Все сложное поведение реализовано в контроллерах.


Эскиз:

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


Пример взаимодействия согласно образцу «Сценарий транзакции»:

Объект-контроллер ведет транзакцию (сохранение расписания) по сценарию от начала до конца.

Образец «Модель предметной области»

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


Эскиз:

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


Пример: в системе регистрации на курсы бизнес-логика распределена между классами-сущностями

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


Если бы применялся сценарий транзакции, взаимодействие выглядело бы так:

В этом варианте всю транзакцию ведет объект-контроллер.

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

• дублирования одинаковых обязанностей в различных классах;

• противоречивых обязанностей в рамках класса;

• классов с одной обязанностью или вообще без обязанностей;

• классов, взаимодействующих с большим количеством других классов.

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


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

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

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

Квалификация механизмов анализа состоит в том, что:

Механизмы анализа играют следующие роли:


Так, имея дело с устойчивыми классами, достаточно указать для них механизм «persistencу», не задумываясь как именно будет реализовано сохранение их экземпляров в базе данных. Пример:

В описания классов, сопоставленных с архитектурными механизмами, добавляются дополнительные характеристики, например:

Характеристики класса Schedule:

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

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

По окончании модель анализа должна быть подвергнута проверке:

Упоминаемые в лекции образцы подробно описаны в книгах [3] и [4].

Литература к лекции 7


Лекция 8. Анализ и проектирование программного обеспечения. Проектирование ПО

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

Этапы проектирования:

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

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


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

В качестве примера рассмотрим механизм Persistency - хранение экземпляров устойчивых классов в БД. Предположим, что в проекте системы регистрации в качестве языка программирования используется Java. Поскольку существующая система каталога курсов функционирует на основе реляционной СУБД, механизмом проектирования, обеспечивающим доступ к этой внешней базе данных, будет RDBMS (Relational Database Management System), реализовать который можно решением JDBC (Java Database Connectivity).

Рис. Диаграмма классов, отображающая механизм JDBC и роли в нем

Стереотип <<role>> используется для элементов модели, являющихся метками-заполнителями (placeholders), - своего рода гнезд, в которые при проектировании будут подставлены реальные элементы, созданные разработчиком системы. Роли являются своего рода параметрами механизма, при подстановке на их место конкретных классов определяется экземпляр механизма, используемый при проектировании системы.

Рис. Изображение механизма JDBC


в виде параметризованной кооперации.


Рис. Диаграмма классов, отображающая структурные связи классов-участников JDBC

Классы-участники механизма JDBC:

PersistencyClient - роль, представляющая любой клиентский класс.

Взаимодействие экземпляров классов, в рамках механизма описывается диаграммами взаимодействия, например:


Из диаграммы видно, что для создания новых данных (нового экземпляра устойчивого класса) экземпляр клиентского класса PersistencyClient запрашивает DBClass. DBClass создает новый экземпляр PersistentClass, запрашивает начальные значения его атрибутов (записанные конструктором). Затем DBClass создает новый оператор SQL, используя операцию createStatement() класса Connection. Этот запрос должен добавить новую запись в таблицу. В результате выполнения этого оператора SQL данные нового экземпляра устойчивого класса помещаются в БД.

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


Первым действием архитектора при выявлении проектных элементов является преобразование классов анализа в проектные элементы.

По каждому классу анализа принимается одно из двух решений:

Совокупности проектных классов объединяются в пакеты или подсистемы. При объединении классов в пакеты учитывается, что:

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

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

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


Несколько классов могут быть объединены в подсистему если:


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

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

При создании подсистем в модели выполняются следующие преобразования:

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


В качестве примера (для системы регистрации) приведем подсистему BillingSystem, которая создана вместо граничного класса BillingSystem. Взаимодействие с ней осуществляется через объект-посредник класса BillingSystem, реализующего интерфейс iBillingSystem. На диаграмме показаны внешние связи подсистемы.

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

Пример формирования архитектурных уровней (система регистрации на курсы):

На диаграмме указаны зависимости между пакетами.

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

• необходимо распределение обработки между различными процессорами или узлами;

• система управляется потоком событий;

• вычисления в системе обладают высокой интенсивностью;

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

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

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

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

Необходимость создания потоков в системе регистрации курсов диктуется следующими требованиями:

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

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

Реализация процессов и потоков обеспечивается средствами операционной системы.

Для моделирования структуры потоков управления используются так называемые активные классы - классы со стереотипами <<process>> и <<thread>>. Активный класс владеет собственным процессом или потоком и может инициировать управляющие воздействия. Связи между процессами моделируются как зависимости. Потоки могут существовать только внутри процессов, поэтому связи между процессами и потоками моделируются как композиции. Модель потоков управления помещается в пакет Process View (представление процессов - одно из архитектурных представлений в модели «4+1»). В качестве примера приведена диаграмма классов, описывающая структуру процесса регистрации студента на курсы. Обратите внимание, что все классы на ней являются активными (выделены двойными вертикальными границами).

Активные классы, показанные на этих диаграммах, выполняют следующее назначение:

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

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

• CourseCatalogSystemAccess - управляет доступом к системе каталога курсов. Один и тот же объект данного класса используется всеми пользователями при доступе к каталогу курсов.

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

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

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

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

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

Распределенная сетевая конфигурация системы моделируется с помощью диаграммы размещения. Диаграмма размещения - это единственная диаграмма, входящая в состав представления размещения - одного из архитектурных представлений, входящих в модель «4+1» Основные элементы диаграммы размещения:

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

Пример - диаграмма размещения для системы регистрации на курсы:


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

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

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

После уточнения реализаций вариантов использования осуществляется проектирование подсистем. Оно включает в себя следующие действия:


Пример проектирования подсистемы CourseCatalogSystem:

Рис.Диаграмма кооперации, описывающая реализацию интерфейсной операции getCourseOfferings() в подсистеме CourseCatalogSystem.

Заметим, что реализация подсистемы CourseCatalogSystem выполнена созданием экземпляра механизма JDBC, т. е. подстановкой класса DBCourseOffering вместо роли DBClass, CourseOffering - вместо PersistentClass, CourseOfferingList - вместо PersistentClassList, CourseCatalogSystem вместо PersistencyClient. То есть, берется шаблонная диаграмма взаимодействия из модели механизма и уточняется подстановкой конкретных классов. Аналогично будет получена диаграмма классов, описывающая структурные связи подсистемы. Подстановка классов на месте ролей изображена на рис.:

Еще одним важным моментом является использованное на диаграмме соглашение моделирования: Вызов операции getCourseOfferings(), реализацию которой мы описываем, производится клиентским объектом (названным CourseCatalogSystemClient) не имеющим указания класса. В самом деле, подсистема ничего не знает о своих клиентских классах, так что указать класс невозможно. По смыслу на месте объекта CourseCatalogSystemClient может быть либо экземпляр RegistrationController, либо экземпляр CloseRegistrationController.


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

Рис. Связывание образца JDBC с конкретными классами системы

После проектирования подсистем производится проектирование классов, которое включает следующие действия:

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

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

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

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

Обязанности классов, определенные в процессе анализа и документированные в виде «операций анализа», преобразуются в операции, которые будут реализованы в коде. При этом:

Уточнение атрибутов классов заключается в следующем:

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


В процессе проектирования связи между классами подлежат уточнению.

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

Примеры: университет -> факультет -> кафедра; здание -> этаж здания.

Виды агрегаций:

Примеры: автомобиль -> колесо; предприятие -> сотрудник.

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

Для множественных полюсов указываются типы: множество {set}, упорядоченное множество {ordered}, мультимножество {bag}, упорядоченное мультимножество {sequence}. На диаграммы могут быть явно указаны классы-контейнеры (список, хэш-таблица и проч.). Классам с необязательными связями добавляются операции проверки, существования соединения между их экземплярами.

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

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

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

Литература к лекции 8


Лекция 9. Проектирование баз данных

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

Основные понятия реляционной модели:

База данных - долговременное самодокументированное хранилище данных. Самодокументация = схема данных, хранящаяся в БД.

Система управления базами данных (СУБД) - ПО доступа к данным. Обеспечивает:

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

Оператор SQL - предложение SQL для манипуляции данными (выборка, изменение, добавление, удаление).

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

Виды ограничений:

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

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

Внешний ключ - ссылка из другой таблицы на возможный ключ.

Пример:


Первичные ключи: persID в 1-ой таблице, compID - во 2-ой. Внешний ключ - столбец employer 1-ой таблицы.

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

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

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

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

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

Переводить модель классов в схему БД предлагается в 3 этапа: отобразить классы в таблицы, отобразить ассоциации и отобразить связи обобщения.

Отображение классов


Пример:

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


Отображение бинарных ассоциаций:

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


«0..1 к 0..1» - рекомендуется отдельная таблица для связи. Ее столбцы - внешние ключи для таблиц классов, связанных ассоциацией. Основной ключ - комбинация этих столбцов.

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

«* к *» - для ассоциации заводится отдельная таблица. Ее столбцы - внешние ключи для таблиц классов, связанных ассоциацией. Основной ключ - комбинация этих столбцов.


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


Пример:

«0..1 к *» - применяются те же решения, что и в «0..1 к 0..1».

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

Отображение классов связанных N-арной ассоциацией: Требуется таблица для хранения связи. Например, в случае тернарной (N=3) связи формируются четыре таблицы, по одной для каждого класса и одна для связи. Таблица связи будет иметь среди своих атрибутов ключи каждой из 3х других таблиц, все её столбцы - её первичный ключ.


Пример:

Отображение классов-ассоциаций:

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


Пример:

Отображение квалифицированных ассоциаций:

Пример:


Отображение обобщения (наследования):

Стратегии:

Какой именно способ выбрать диктуют соображения эффективности (скорость в обмен на объем памяти). Рассмотрим на примере. Пусть класс Person - абстрактный.

При использовании 1-го подхода будут созданы 4 таблицы. В первой будут храниться значения атрибутов, специфичных для персон, во второй - для студентов, в третьей - для сотрудников, в четвертой - для работающих студентов. У таблиц Person, Student, Employee будет дополнительный столбец - тип, в котором будет храниться реальный тип объекта. Для каждого экземпляра класса Student будут две записи, одна в таблице студентов, вторая - в таблице персон. Для экземпляра StudentEmployee - четыре записи, по одной в каждой таблице. При чем у всех их будет одинаковое значение первичного ключа - идентификатора. Рассматривая запись из таблицы персон можно узнать реальный тип текущего объекта и по значению идентификатора найти в других таблицах значения дополнительных атрибутов этого объекта. При таком подходе между таблицами есть ограничения целостности, например, если удаляется запись о персоне, следует удалить связанные записи из других таблиц.


Рис. Пример применения стратегии «Для каждого класса своя таблица».

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


Рис. Пример применения стратегии «Для всей иерархии одна таблица».

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


Рис. Пример применения стратегии «Таблицы только для конкретных классов».

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


Рис. Пример применения стратегии «Таблицы только для различных конкретных классов».

Для изображения схем БД в виде диаграмм классов применяется специализированный набор стереотипов - профиль.

Таблица изображается как класс со стереотипом <<table>>. SQL-запросы также изображаются классами со стереотипом <<view>> (на диаграмме отсутствуют). Столбцы таблиц представлены атрибутами классов-таблиц. Используются стереотипы <<column>> (обычный столбец) <<PK>> (столбец, входящий в первичный ключ), <<FK>> (столбец, входящий во внешний ключ), <<PK,FK>> (столбец, входящий в первичный и во внешний ключ помечается двумя стереотипами). «Операции» таблиц моделируют ограничения или хранимые процедуры. Связи между таблицами моделируются как ассоциации между классами. Стереотипы связей <<identifying>> (идентифицирующая), <<non-identifying>> (не идентифицирующая). Связь является идентифицирующей, если первичный ключ связанной таблицы включает в себя её внешний ключ. В остальных случаях она не идентифицирующая. Для отображения ограничений целостности связь может моделироваться композицией. Направления связей не указывают, так как связи между таблицами всегда двунаправлены.

Рассмотрим примеры.

В системе обработки заказов есть два устойчивых класса: Order и OrderItem:


Так как мощности композиции «1 к 1..*», дополнительная таблица не нужна. Схема БД, полученная при объектно-реляционном отображении, выглядит так:


Согласно схеме БД, каждая запись о заказе состоит из 6 столбцов, один из которых является первичным ключом (<<PK>>). Столбцы для хранения статических и выводимых атрибутов не заводятся, их можно вычислять запросами. Как "операция" таблицы TableOrder моделируется ограничение первичного ключа. Позиции заказа хранятся в отдельной таблице TableOrderItem. В этой таблице 5 столбцов, один из которых является частью первичного ключа (<<PK>>), а другой - и частью первичного ключа, и внешним ключом (<<FK>>). В таблицу добавлены ограничения первичного и внешнего ключа и ограничение индекса. Связь между таблицами идентифицирующая, так как часть первичного ключа входит во внешний ключ. Одному объекту - экземпляру класса Order - будут соответствовать одна запись в таблице TableOrder и связанные с ней записи в таблице TableOrderItem.

Другой пример из системы регистрации на курсы. Диаграмма с устойчивыми классами:

Схему БД получим, используя стратегию «для каждого класса своя таблица» и не объединяя таблицы студентов и классификаций (хотя связь «1 к 1» позволяет это сделать). Полученная в результате объектно-реляционного отображения схема выглядит так:


Согласно схеме БД, каждая запись о студенте состоит из 3 столбцов, один из которых является первичным ключом (<<PK>>). Как «операция» таблицы TableStudent моделируется ограничение первичного ключа. Классификация студента хранится в отдельной таблице TableClassification. В этой таблице единственный служебный столбец, являющийся и первичным, и внешним ключом (<<PK,FK>>). В таблицу добавлены ограничения первичного и внешнего ключа, ограничение индекса и ограничение уникальности, определяющее, что с каждой записью классификации связана ровно одна запись из одной из двух оставшихся таблиц. Эти две таблицы для подклассов. Помимо собственных столбцов в них есть служебный, являющийся и первичным и внешним ключом (два стереотипа <<PK>>, <<FK>>). В каждой из двух таблиц добавлены 3 «операции»-ограничения: индекс, первичный и внешний ключ. Все связи идентифицирующие, так как всюду первичный ключ входит во внешний.

Одному объекту - экземпляру класса Student - будут соответствовать три записи:

Можно упростить схему, убрав таблицу TableClassification и связав таблицу TableStudent с таблицами TableFullTimeClassification и TablePartTimeClassification напрямую.

Литература к лекции 9


Лекция 10. Образцы проектирования

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

Фактически, каждый паттерн является формализованным опытом лучших разработчиков в индустрии создания ПО. Исторически понятие образца возникло в архитектуре, введено архитектором Кристофером Александром - проектировщиком зданий и городов в конце 1970-х.

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

Основные составляющие части образца:

Имя. Идентифицирует образец, Хорошее имя характеризует решаемую проблему и способ ее решения.

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

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

Результаты. Следствия применения паттерна и компромиссы. Преимущества и недостатки образца. Влияние использования образца на гибкость, расширяемость и переносимость системы.

Полное описание образца в сборнике Гаммы и др. («банды четырех») включает:

Каталог образцов содержит 23 образца. Существуют другие каталоги - например, каталог Фаулера.

Классификация образцов:

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

Абстрактная фабрика (Abstract Factory)

Классификация: образец порождения объектов.

Назначение: предоставляет интерфейс для создания взаимосвязанных и взаимозависимых объектов, не определяя их конкретных классов.

Мотивация: часто встает задача проектирования программной системы независимой от конкретной реализации GUI.

Ситуации применимости:


Участники:

Отношения:

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


Результаты:

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

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


Фабричный метод (Factory method)

Классификация: образец порождения объектов.

Назначение: определяет интерфейс для создания объектов, но оставляет реализациям решение о том, какой объект какого именно класса создавать.

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

Ситуации применимости:


Участники:

Отношения:

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

Результаты:


Пример (редактор документов, фабричный метод createDoc(), конкретный тип создаваемых документов определяется в реализации фабричного метода в подклассе):


Другой пример - приложение, настраиваемое на работу с одной из двух СУБД (фабричный метод makeDB()):

Адаптер (Adapter)

Классификация: структурный образец.

Назначение: преобразует один интерфейс в другой, обеспечивая совместимость.

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

Ситуация применимости: обеспечение совместимости существующего класса при его повторном использовании.

Участники:

Адаптер объектов (агрегация от адаптера к адаптируемому классу)

Отношения:


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

Результаты:


Адаптер класса (Adaptee - класс, не интерфейс, вместо агрегации обобщение):

Отношения:

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


Результаты:

Composite (Компоновщик)

Классификация: структурный образец.

Назначение: организует объекты в древовидные структуры, позволяет клиентам единообразно работать с составными и элементарными объектами.

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

Ситуации применимости:


Участники:

Диаграмма объектов демонстрирует пример составной структуры, соответствующей паттерну:


Отношения:

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

Результаты:


Рассмотрим пример. Диаграмма классов (абстрактный класс заменен интерфейсом, наследование - связями реализации):

Диаграмма объектов:

Диаграмма последовательности:

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

Мост (Bridge)

Классификация: структурный образец.

Назначение: отделить абстракцию от реализации.

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

Ситуации применимости:


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

Участники:

Отношения:

Абстракция перенаправляет запросы клиента одной из реализаций Implementora.

Результаты:

Пример: Пусть есть абстракция Shape (форма), в ней есть операция draw(), отвечающая за отрисовку. В каждой конкретной форме (Rectangle, Circle) отрисовка реализуется с помощью примитивов drawLine(), drawCircle(), описанном в интерфейсе Drawing, реализуемом разными графическими пакетами DrawingV1, DrawingV2, рассчитанными на работу с разными графическими устройствами Driver1, Driver2. Диаграмма классов:


Диаграмма взаимодействия:


Если не применять образец, то у Rectangle и Circle могли бы быть два наследника, каждый из которых рассчитан на работу с одним из двух графических устройств. Т. е. в иерархии форм было бы 7 классов. Если добавить ещё формы - наследницы Shape - Triangle, PolyLine, то в первом случае при их отрисовке дополнительные классы не нужны, так как можно воспользоваться реализациями Drawing. Во втором случае иерархия разрастается, в ней становится 13 классов. Аналогично применение паттерна Мост выгодно при добавлении поддержки еще одного графического устройства. Будет достаточно добавить новую реализацию интерфейса Drawing, вместо того, чтобы заводить каждой конкретной форме наследника с реализацией отрисовки для нового устройства.

Фасад (Facade)

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

Идея продемонстрирована на диаграмме (без фасада клиентский класс имел бы связи со всеми классами подсистемы):


Пример: подсистема BillingSystem системы регистрации на курсы реализует доступ к внешней расчетной системе. Прокси-класс скрывает детали подсистемы, реализует её интерфейс. Реализация операции заключается в формировании параметра для вызова метода BillingSystemInterface::submit(theTransaction) и самого вызова этой операции.

Диаграмма взаимодействия объектов при использовании образца Фасад.

Proxy(Заместитель)

Классификация: структурный образец.

Назначение: для объекта создается суррогат, контролирующий доступ.

Мотивация: есть «тяжелый» класс, объекты которого разумно создавать по требованию - эта обязанность возлагается на легкие суррогаты.

Ситуации применимости:


защищающий заместитель (проверяет права доступа).

Участники:

Отношения:


Заместитель получает запрос клиента и переадресует его реальному классу. Детали зависят от вида заместителя.

Результаты (зависят от вида заместителя):

Цепочка обязанностей (Chain of Responsibility)

Классификация: образец поведения.

Назначение: избежать привязки отправителя запроса к получателю, давая возможность передать запрос через многих (заранее неизвестно скольких) посредников.

Ситуации применимости:


адресат запроса не указан, но один из группы;

Участники:

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

Результаты:

Iterator (Итератор)

Классификация: образец поведения.

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

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

Участники:


ConcreteAggregate - реализация интерфейса Aggregate.

Результаты:

Strategy (Стратегия)

Классификация: образец поведения.

Назначение: Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми.

Мотивация: есть несколько алгоритмов решения одной задачи, которые нежелательно «зашивать» в клиентский класс.

Ситуации применимости:

Участники:

Результаты:


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

Предполагается, что объект класса Configuration сообщает SalesOrderссылку на объект-алгоритм расчета налогов (либо экземпляр USTax, пригодный для США, либо CanTax, пригодный для Канады). Если потребуется добавить новые способы расчета, достаточно добавить подклассы CalcTax. Обратите внимание, что в примере вместо интерфейса и реализации используется абстрактный класс и наследование.

Альтернативой предложенному решению является внесение внутрь SalesOrder::calcTax() логики выбора схемы расчета и реализация расчетов в отдельных операциях SalesOrder. Модифицируемость такого решения ниже.

Decorator (Декоратор)

Классификация: структурный образец.

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

Мотивация: Например, хотим, чтобы библиотека GUI могла добавлять свойства (рамку) или новое поведение (прокрутку) к любому элементу GUI. Для этого «оборачиваем» элемент GUI в объект-декоратор. Декоратор имеет тот же интерфейс. Он переадресует запросы элементу, который в него «завернут».

Ситуации применимости:

Участники:

Результаты:


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


Диаграмма кооперации, демонстрирующая цепочку объектов, задействованных при печати:

Observer(Наблюдатель)

Классификация: образец поведения.

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

Мотивация: таблица и связанные диаграммы, «издатель» и «подписчики» и т. п..

Ситуации применимости:

Участники:


Взаимодействие объектов при оповещении:

Результаты:

Приведем пример использования образца для реализации оповещения при изменении данных о клиенте. Например, изменение состояния (State) может влиять на отправку писем клиенту. Если вызывается Customer::setState(), то в теле его метода срабатывает вызов Customer::notify(), при обработке которого будут вызваны операции update() у всех наблюдателей подписавшихся на изменения в конкретном экземпляре класса Customer.


Менее подходящим решением является явный вызов операций нужных объектов из тела метода, обновляющего объект Customer, так как возникают явные зависимости между классами:


Литература к лекции 10


Лекция 11. Технология создания программного обеспечения. Rational Unified Process (RUP)

Основные определения:

Технология создания ПО (ТС ПО) - это упорядоченная совокупность взаимосвязанных технологических процессов в рамках ЖЦ ПО.

Технологический процесс - это совокупность взаимосвязанных технологических операций.

Технологическая операция - это основная единица работы, выполняемая определенной ролью, которая:

Рабочий продукт - информационная или материальная сущность, которая создается, модифицируется или используется в некоторой технологической операции (модель, документ, код, тест и т.п.).

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

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


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

Основным требованием, предъявляемым к современным ТС ПО, является их соответствие стандартам жизненного цикла (ЖЦ) ПО и оценкой технологической зрелости организаций-разработчиков (ISO 12207, ISO 9000, CMM и др.). Согласно этим нормативам, ТС ПО должна поддерживать полный набор процессов ЖЦ, к которым относятся:

Полнота поддержки процессов ЖЦ ПО должна поддерживаться комплексом инструментальных средств (CASE-средств). Также есть ряд других требований:

В качестве примера ТС ПО рассмотрим Rational Unified Process (RUP).


RUP является развитием процесса разработки, принятого в компании Ericsson в 70-х-80-х годах XX века. Эта модель была создана Джекобсоном (Ivar Jacobson), впоследствии, в 1987, основавшим собственную компанию Objectory AB именно для развития технологического процесса разработки ПО как отдельного продукта, который можно было бы переносить в другие организации. После вливания Objectory в Rational в 1995 разработки Джекобсона были интегрированы с работами Ройса (Walker Royce), Крачтена (Philippe Kruchten) и Буча (Grady Booch), а также с развивавшимся параллельно универсальным языком моделирования (Unified Modeling Language, UML).

RUP в значительной степени соответствует указанным выше требованиям. Основными принципами RUP являются:

На следующей странице на рисунке показано общее представление RUP в двух измерениях («диаграмма с горбами»):

Форма горбов является примерной, не воспринимайте диаграмму буквально.

Динамический аспект

Согласно технологии RUP, ЖЦ ПО разбивается на отдельные циклы, в каждом из которых создается новое поколение продукта. Каждый цикл, в свою очередь, разбивается на четыре последовательные стадии:

начальная стадия (inception);

стадия разработки (elaboration);

стадия конструирования (construction);

стадия ввода в действие (transition).

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


Рис. Общее представление RUP

Каждая стадия завершается в четко определенной контрольной точке (milestone). В этот момент времени должны достигаться важные результаты и приниматься критически важные решения о дальнейшей разработке.

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

Результатами начальной стадии являются:

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

Результатами стадии разработки являются:

Стадия разработки занимает около пятой части общей продолжительности проекта.

RUP представляет собой итерационный процесс разработки, в котором система разрабатывается и реализуется по частям. На стадии конструирования построение системы выполняется в течение нескольких итераций. Каждая итерация является своего рода мини-проектом. На каждой итерации для конкретных вариантов использования выполняются анализ, проектирование, кодирование, тестирование и интеграция («мини-каскад»). Итерация завершается демонстрацией результатов пользователям и выполнением системных тестов для контроля корректности реализации.

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

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

Результатом стадии конструирования является начальная эксплуатационная версия программного продукта, готовая к передаче конечным пользователям. В её составе содержится как минимум следующее:

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


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

Примерное соотношение по трудоёмкости (в человеко-часах) между стадиями представлено на диаграмме.

Статический аспект

Статический аспект RUP представлен четырьмя основными элементами:

Понятие «роль» (role) определяет поведение и ответственность личности или группы личностей, составляющих проектную команду. Одна личность может играть в проекте много различных ролей.

Видом работы конкретного исполнителя понимается элементарная работа - технологическая опреация, выполняемая им.

Дисциплина (discipline) соответствует понятию технологического процесса (или процесса ЖЦ) и представляет собой последовательность работ, приводящую к получению значимого результата.

В рамках RUP определены шесть основных дисциплин:

и три вспомогательных:

Можно видеть, что процессов ЖЦ в RUP меньше, чем в стандарте ISO 12207. например, отсутствует сопровождение.

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

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

Полученные модели служат основой для моделей требований и анализа. Подробно дисциплина рассматривалась на лекции «Моделирование бизнес-процессов».

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

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

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

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

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

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

За реализацию кода отвечает инженер по компонентам.

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

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

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

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

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

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

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

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

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

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

Литература к лекции 11

Понятие безнадежного проекта введено Эдвардом Йорданом. См. Эдвард Йордон. Путь камикадзе. 2-е изд. - М.: Лори, 2004

Брукс Ф. Мифический человеко-месяц или как создаются программные системы. - СПб.: Символ-Плюс, 1999

Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. - СПб.: Питер, 2007.

Фаулер М. Архитектура корпоративных приложений. - М.: Вильямс, 2007.