Анализ требований. Завершение SRS: добавление детальных требований

Анализ требований. Завершение SRS: добавление детальных требований

Детальные требования (D-требования) — единственный документ, в котором определяется конкретная природа программы. Уровень детализации должен быть полным, но не чрезмерным.
Содержание этой главы в контексте процесса разработки программы показано на рис. 4.1.
♦ Основы: разделы 4.1-4.5.
♦ Детали: разделы 4.6-4.10.
♦ Руководство по учебному проекту: Определение D-требований для игры Встреча.
♦ Упражнения.
♦ Пример: Спецификация требований к программному обеспечению (SRS), часть 2.

Анализ требований. Завершение SRS: добавление детальных требований

Детальные требования (D-требования) — единственный документ, в котором определяется конкретная природа программы. Уровень детализации должен быть полным, но не чрезмерным.
Содержание этой главы в контексте процесса разработки программы показано на рис. 4.1.
♦ Основы: разделы 4.1-4.5.
♦ Детали: разделы 4.6-4.10.
♦ Руководство по учебному проекту: Определение D-требований для игры Встреча.
♦ Упражнения.
♦ Пример: Спецификация требований к программному обеспечению (SRS), часть 2.
Учебные цели этой главы заключаются в следующем.
♦ Овладеть разными вариантами организации D-требований:.
♦ по классам;.
♦ по вариантам использования;.
♦ по характеристикам;.
♦ по событиям.
♦ Суметь выполнить требования:.
♦ соблюсти достаточную степень детализации, чтобы суметь завершить проектирование и реализацию;.
♦ уметь выражать нефункциональные требования, например производительность.
Рис. 4.1. Схема процессов разработки программ: темы главы
Основы.
4.1. Введение в детальные требования 4.1.1. Значение детальных требований.
Разработчикам программного обеспечения нужна база для проектирования и разработки. Эта база состоит из детальных требований. Их также называют конкретными требованиями, функциональными спецификациями, требованиями разработчика или D-требованиями. D-требования состоят из полного списка конкретных свойств и функциональности, которую должна иметь программа, сформулированных в подробностях. Каждое из этих требований пронумеровано, помечено и отслеживается по ходу разработки. D-требования должны быть согласованы с С-требованиями.
Предполагается, что D-требования будут читать преимущественно разработчики. Заказчики также заинтересованы в них и обычно могут понять или прокомментировать большинство из них. Вспомните, что основная аудитория для С-требований состоит из заказчиков.
Когда дело доходит до разработки программы, становится ясно, что «дьявол кроется в деталях». Например, в 1999 году NASA потеряла спутник стоимостью в сотни миллионов долларов из-за того, что данные, которые были восприняты в метрической системе, на самом деле были записаны в другой [14]. Самое удивительное здесь в том, что этот дефект был обнаружен лишь в день катастрофы. Как жаль, что он не был обнаружен на стадии разработки! На первой линии зашиты от искажения или пропуска деталей стоят D-требования. Формулирование всех требований со всеми деталями — далеко не тупое занятие, как это может показаться на первый взгляд — включает в себя сложную задачу организации людей и документации. Чтобы понять эту задачу, представьте себе проблему организации документа требований в 20 томах так, чтобы, например, инженер NASA точно знал, где искать какое-то конкретное требование.
4.1.2. Типичная схема процесса анализа D-требований.
Типичная последовательность действий для сбора и документирования D-требований показана на рис. 4.2. В разделе 4.5 описаны способы организаций детальных требований. D-требования создаются из С-требований, как показано в разделе 4.3. В идеале мы начинаем писать тесты для каждого из детальных требований одновременно с написанием самих требований. Хотя D-требования пишутся преимущественно для разработчиков, требования и их тесты просматриваются также и заказчиком. Затем D-требования должны быть проверены и выпущены (раздел 4.6.3).
Рис. 4.2. Схема процессов составления D-требований
4.2. Типы D-требований.
Существуют несколько типов требований.
1.
Функциональные требования:.
♦ функциональность приложения.
2.
Нефункциональные требования.
1) Производительность:.
♦ скорость;.
♦ пропускная способность (трафик);.
♦ использование памяти (оперативная память, жесткий диск).
2) Надежность и доступность.
3) Обработка ошибок.
4) Интерфейсные требования.
Как программа взаимодействует с пользователем и с другими программами.
5) Ограничения:.
♦ точность;.
♦ ограничения на инструменты и язык, например «должен использоваться Fortran 88»;.
♦ ограничения проектирования;.
♦ стандарты, которые должны быть использованы;.
♦ платформы, которые должны быть использованы.
3.
Обратные требования. Чего программа не делает.
Эта классификация применима как к С-, так и к D-требованиям. Во время написания С-требований эти различия часто занимают второе место после формирования основных идей заказчика относительно программы в целом. Однако классификация становится более важной при написании D-требований, поскольку она руководит процессами разработки и тестирования в разных аспектах. Приведенная классификация представляет собой модификацию схемы [92]. Далее в этой главе описаны разные типы требований.
4.2.1. Функциональные требования.
Функциональные требования определяют работу, которую должно выполнять программное приложение (например, «приложение будет вычислять стоимость портфеля акций пользователя»). С другой стороны, требование, такое как «приложение будет выполнять вычисление стоимости каждого портфеля акций менее чем за одну секунду» не является функциональным, поскольку оно не определяет конкретную работу. Вместо этого оно оценивает работу или работы (определяет некоторое утверждение о работе).
4.2.2.
Нефункциональные требования: требования к производительности.
Требования к производительности определяют временные ограничения, которые должны быть выполнены в программе. Заказчики и разработчики обсуждают ограничения по времени вычислений, использование оперативной памяти, использование вспомогательных запоминающих устройств и т. д. Например:.
Для любой балки Анализатор давления должен создать отчет типа 5 о давлении менее чем за минуту.
Требования к производительности являются важной частью приложений, работающих в реальном времени, в которых действия должны уложиться в предопределенные временные рамки. Примерами приложений реального времени могут быть программы избежания столкновений, контроля полетов и антиблокировочной тормозной системы.
4.2.3.
Нефункциональные требования: надежность и доступность.
Требования надежности определяют надежность в измеряемых величинах. Требования такого типа предполагают вероятность неидеальной работы программы и ограничивают область ее несовершенства. Например:.
Приложение, управляющее радарами аэропорта, должно давать не более двух ошибок в месяц.
Доступность, близкая по смыслу надежности, оценивает степень, в которой приложение должно быть доступно пользователям. Например:.
Приложение, управляющее радарами аэропорта, должно быть доступно на уровне один или два постоянно как на основном, так и на запасном компьютере. Оно может быть недоступно на одном из этих компьютеров на уровне один или два не более 2% времени в любой 30-дневный период.
4.2.4.
Нефункциональные требования: обработка ошибок.
Эта категория требований объясняет, как программа должна реагировать на возникающие ошибки. Например, что должна делать программа, если она получает сообщение из другой программы в неразрешенном формате? Это не касается ошибок, генерируемых самой программой.
В некоторых случаях обработка ошибок относится к действиям, которые должна предпринять программа, если она сама создала ошибку из-за дефекта в своей конструкции. Этот тип требований к ошибкам должен быть использован выборочно, поскольку нашей целью является создание безошибочных программ, а не исправление наших ошибок с помощью бесконечного кода обработки ошибок.
Например, предположим, что нам нужно определить требования для прибора, который автоматически определяет дозы лекарств для внутривенного введения больному. Мы делаем предположение, что программа будет тщательно определена, разработана, реализована и проверена с тем, чтобы состав лекарств и дозы были корректными. Однако было бы правильно в таком случае определить независимую проверку состава и дозы лекарств перед его выпуском в кровь и соответствующим образом определить обработку ошибок.
В итоге, проверка ошибок в самой программе уместна только для критичных частей программы.
4.2.5.
Нефункциональные требования: интерфейсные требования.
Интерфейсные требования описывают формат, в котором программа общается с окружением.
Стоимость посылки статьи от адресата получателю должна постоянно показываться в текстовом окне «Цена».
Формат, используемый для передачи сообщений «Ожидаемая статья» для взаимодействия с почтовыми кампаниями, будет представлять собой строку вида exp
, где
— это строка из Таблицы стандартов городов.
В первом примере показано требование пользователей программы. Второе предоставляет формат сообщения для обмена информацией с другими программами. Оба требования являются интерфейсными.
4.2.6.
Нефункциональные требования: ограничения.
Ограничения на проектирование или реализацию описывает границы или условия того, как приложение должно быть задумано и разработано. Эти требования не должны использоваться вместо процесса проектирования — они всего лишь определяют условия, наложенные на проект заказчиком, а также окружение или другие обстоятельства. Например, сюда можно отнести точность:.
Вычисления оценки ДТП системой AEF должны быть выполнены с точностью до одного сантиметра.
Часто также накладываются ограничения по инструментам и языкам. Сюда относятся исторически сложившиеся традиции организации, совместимость и опыт программистов.
Система AEF должна быть разработана на Fortran 88.
Ограничения на проектирование накладываются в связи с тем, что этого могут требовать финансово заинтересованные в проекте лица. Такие условия ограничивают свободу проектирования разработчиков, например:.
Система AEF должна использовать систему UCF для демонстрации результатов столкновения.
Ограничение, требующее следовать определенному стандарту, часто определяется политикой фирмы или заказчиком.
Документация AEF должна удовлетворять требованиям Федерального стандарта 1234.56.
Код AEF должен быть документирован с использованием руководства кампании для документирования кода, версия 5.2.
Проекты часто ограничены платформами, на которых они будут использоваться. Например:.
Система AEF должна работать на компьютерах Ajax 999 модель 12345 со 128 Мбайт оперативной памяти и 12 Гбайт дискового пространства.
4.2.7.
Обратные требования.
Обратные требования определяют, чего программа не будет делать. Логично, что существует бесконечное число обратных требований: мы выбираем только те, которые разъясняют возможное непонимание. Например:.
Система AEF (см. предыдущий раздел) не обязательно должна анализировать данные ДТП.
4.2.8.
Отображение типов требований на стандарт IEEE 830-1993.
Основные разделы стандарта IEEE 830-1993 с их отображением на типы требований, приведенные выше, перечислены на рис. 4.3.
Рис. 4.3. Организация SRS согласно стандарту IEEE 830-1994: детальные требования с объектно-ориентированной организацией
4.3. Желательные свойства D-требований.
Мы хотели бы подчеркнуть значительное влияние, которое может оказать на проект пропуск тех или иных деталей. Чтобы помочь убедиться, что все детали были охвачены, мы определим свойства, которыми должны обладать D-требования.
В частности, D-требования должны быть полными и согласованными. Нужно, чтобы можно было отследить каждое требование в проекте, протестировать его работу и разработать его согласно его приоритету. Мы будем исследовать требования на наличие этих характеристик, когда будем оценивать качество (раздел 4.6). В конце данного раздела представлена последовательность шагов, которая может быть использована для выражения подробных требований.
4.3.1. Прослеживание.
4.3.1.1. Прослеживание функциональных требований.
Представьте себе программу с 1000 детальных требований. Без возможности четкого контроля каждого требования от проекта программы до самого кода, реализующего это требование, было бы сложно убедиться в том, что программа разработана в соответствии с требованиями. Когда требования меняются (чего следует ожидать), это становится еще сложнее. Возможность отображать каждое требование на соответствующие части проекта и программы называется прослеживанием. Один из способов, помогающих достичь этого, заключается в отображении каждого функционального D-требования на конкретную функцию целевого языка. Эта техника использована в примере (рис. 4.4): показаны части проекта, которые должны быть объединены для возможности контроля. Достижение и поддержка этого уровня контроля во время разработки представляет собой сложную задачу.
В качестве примера представьте себе такое требование к игре Встреча.
Когда внешний игровой персонаж появляется в зоне, содержащей главный персонаж игрока, или наоборот, эти персонажи вступают в контакт друг с другом.
Значение этого утверждения очевидно, однако нужно понимать, какая часть проекта и кода будет отвечать за выполнение этого требования. При использовании объектно-ориентированного подхода мы можем связать это требование с конкретной функцией конкретного класса. Вопрос о том, какой класс отвечает за эту функцию, нетривиален, и он возникает постоянно при использовании объектноориентированного подхода. Что касается приведенного выше примера, объекты Зона должны иметь возможность распознавать контакт, поскольку именно эти объекты предположительно должны владеть информацией о находящихся «в них» персонажах. В частности, требование можно будет отследить с помощью определенного кода, обрабатывающего события для класса Зона.
По мере продвижения проекта документ с требованиями должен поддерживаться согласованным с архитектурой и реализацией. Когда требования сложно отследить в архитектуре и коде, разработчики часто стараются избежать обновления документа требований при внесении изменений в исходный код, поскольку для этого потребуется много усилий. В конце концов, такая порча документа приводит к увеличению работ и затрат на проект. Это явление проиллюстрировано в нижеследующем примере.
1. Разработчику Биллу поручили сделать изменения в программе. Биллу кажется, что привести документ с требованиями в соответствие с кодом сложно, и он решает не делать этого.
2. Разработчику Джону поручают сделать новые изменения. Он делает изменения в коде, тестирует их и начинает обновлять документ с требованиями. Но все говорят ему не беспокоиться об этом, поскольку документ устарел в некоторых местах и никто ему уже не доверяет. Джону говорят, что нет смысла делать это, поскольку документ все равно никто читать не будет. Так что Джон бросает это занятие и продолжает свое программирование. Таким образом, расхождения между документом и кодом увеличиваются.
Рис. 4.4. Контроль D-требования (стрелки показывают основные линии прослеживания)
Даже самые добросовестные разработчики игнорируют необходимость обновления документа требований, если документ в целом считается ненадежным. С другой стороны, если документы четко и просто ссылаются друг на друга и руководство делает документирование обязательным, разработчики поддерживают документы в очень хорошей профессиональной форме. Другими словами, система, используемая для поддержания соответствия D-требований с проектом и соответствующим кодом, должна быть очень четкой и конкретной.
Когда код, осуществляющий требование, находится в нескольких местах, прослеживание достигается посредством использования матрицы прослеживания требований, пример которой приведен в табл. 4.1.
Таблица 4.1. Матрица прослеживания требований
Требование
Модуль 1
Модуль 2
Модуль 3
1783
showName()
computeBal()
getlnterest()
1784
showAccount()
showAddress()
showName()
Как показано в табл. 4.1, требование 1783 реализовано посредством функций showNameQ в модуле 1, computeBal О в модуле 2 и getlnterestO в модуле 3. Изменение в этом требовании повлечет изменение в одной или нескольких из этих функций. Это следует четко контролировать, поскольку эти функции могут участвовать также и в выполнении других требований (например, showNameO используется еще и для реализации требования 1784). В результате изменения, сделанные для удовлетворения одного требования, могут нарушить другое. Поскольку с отношениями «многие-ко-многим» трудно работать, мы стараемся сделать отображение между требованием и функцией «один-к-одному».
Мы хотим, чтобы каждое D-требование можно было проследить прямо и обратно. Предыдущие обсуждения касались прямого отслеживания от D-требований до исполнения. Обратное отслеживание D-требований означает, что требование представляет собой четкую последовательность одного или нескольких С-требований. Например, D-требование:.
внешний персонаж должен двигаться от зоны к зоне со средним интервалом в пять секунд.
может быть прослежено назад к следующему С-требованию, которое было частью раздела 2.0 в SRS:.
остальные [персонажи], называемые внешними, должны контролироваться программой.
Такой обратный контроль является основой проверки D-требований. Возможность полного прослеживания означает, что каждое D-требование связано с конкретным элементом проекта, а также с тестом элемента (рис. 4.5). Рисунок показывает преимущество жесткого соответствия между каждым отдельным функциональным требованием, описывающей требование частью проекта и реализующей его частью кода. Они связаны с тестом требования (тесты являются темой главы 8).
4.3.1.2. Контроль нефункциональных требований.
Предыдущее обсуждение относилось к функциональным требованиям, однако как же мы прослеживаем нефункциональные требования? Это может оказаться сложной задачей, поскольку одному нефункциональному требованию может соответствовать более одной части проектирования и реализации. Например, требование того, чтобы каждый контакт происходил менее чем за одну секунду, может затронуть код класса Контакт и (или) класса ПерсонажИгры и (или) класса Зона. Наша задача на данном этапе заключается в определении нефункциональных требований возможно более четким языком. Чтобы прояснить нефункциональные требования, мы также затронем вопросы проектирования и реализации.
Рис. 4.5. Прослеживание и тестирование функциональных D-требований
Одна из целей стадии проектирования — изолировать каждое нефункциональное требование в отдельный элемент проекта. В случае требований к производительности делается попытка изолировать самые медленные вычислительные компоненты. Каждую функцию, связанную с требованиями к производительности, сопровождают соответствующие проверяемые нефункциональные комментарии. Желательно, чтобы эти требования были количественными, как, например, «нужно завершить работу менее чем за одну миллисекунду в худшем случае». Аналогично, в случаях, когда определены ограничения по памяти, мы выделяем функции, больше других расходующие память.
Исследования показали, что основную часть работы в программе выполняет относительно небольшая часть функций, поэтому поиск нескольких основных расходующих время функций может принести хорошие результаты. Вернемся к требованию выполнения контакта в игре Встреча за одну секунду, которое уже упоминалось. Во время проектирования и реализации мы ищем типичные компоненты, расходующие время при обработке контакта. К таким компонентам относятся циклы, отображение графики, а также сетевая передача данных. Циклы и передачи данных не связаны с обработкой контакта, поэтому проводится тест. Тест должен убедить нас в том, что графика и графический пользовательский интерфейс, требуемые при контакте, работают достаточно быстро. Функция, расходующая большую часть времени, — это, вероятно, либо функция «Вступить в контакт с внешним персонажем» класса Контакт, либо функция вывода результатов контакта.
Чтобы утвердить нефункциональные требования, нам пришлось связать каждое из них с планом тестирования — желательно делать это во время написания требования. Типичная связь функциональных и нефункциональных требований с реализацией и тестированием, обсуждавшимися выше, представлена на рис. 4.6. На рисунке проиллюстрирован случай, когда нефункциональным требованиям может соответствовать несколько компонентов, а также когда для утверждения нефункциональных требований необходимо системное или интегральное тестирование, поскольку их верификация (то есть проверка до запуска программы) может оказаться весьма сложной.
Рис. 4.6. Прослеживание и тестирование функциональных и нефункциональных требований
4.3.2. Пригодность к тестированию и однозначность.
Должна существовать возможность валидировать требование, убедившись, что требование было корректно реализовано. (Вспомните, что в главе 1 валидация относится к проверке того, что артефакт функционирует так, как должен.) Требования, которые можно протестировать, называются тестируемыми. Нетестируемые «требования» не имеют большой практической ценности. Приведем пример нетестируемого требования и перечислим действия, которые нужно выполнить для того, чтобы требование стало тестируемым.
Система должна показывать разницу в зарплате между зарплатой клиента и средней зарплатой в мире для той же специальности.
Это требование не протестировать, поскольку упоминаемую среднюю зарплату невозможно определить (несмотря на то, что она существует).
Улучшенная версия требования:.
Система будет показывать разницу в зарплате между зарплатой клиента и предполагаемой средней зарплатой для той же специальности согласно цифрам, опубликованными на веб-сайте ООН на время запроса.
Если D-требование записано нечетко или двусмысленно, мы не сможем определить, правильно оно было реализовано или нет. Пример неоднозначного требования и его исправленная версия приведены ниже.
Игрок может определять значения характеристик персонажей игры.
В любое время? Вероятно, нет. Придется протестировать разные случаи, с соответствующими ненужными затратами, и мы получим неправильный результат. Улучшенная версия этого требования.
Каждый раз, когда внешние персонажи отсутствуют в зоне нахождения главного персонажа игрока, игрок может изменить значения характеристик персонажа, сохранив сумму значений неизменной. Для этого используется окно характеристик игрока (см. раздел 3.2.ПИ в конце главы).
4.3.3. Приоритет.
Часто бывает трудно реализовать всю запланированную функциональность программы в срок и не выходя за рамки бюджета. Как упоминалось в разделе 2.3.1, могут измениться возможности, сроки, уровень качества и стоимость. Поэтому, если сроки, бюджет или уровень качества менять нельзя, единственной альтернативой остается изменение возможностей, то есть уменьшение количества реализованных требований. Этот процесс отсеивания выполняется запланированным способом. Одна из техник — расставить приоритеты между детальными требованиями. Упорядочивание всех требований обычно является пустой тратой времени — вместо этого многие организации классифицируют требования по трем (иногда четырем) категориям. Мы назовем их важные, желательные и необязательные. Убедимся, что проект реализует все существенные требования. Использование трех категорий представляет собой применение отбраковки, упомянутой в главе 2. Назначение приоритетов требованиям сказывается на проектировании, поскольку желаемые и необязательные требования часто указывают направление, в котором должно двигаться приложение. Широко известная мудрость гласит, что предприятия получают порядка 80 % дохода от использования всего лишь 20 % требований. Поэтому, если приоритеты расставлены правильно (например, только около 20 % требований отнесены к существенным), можно получить максимальную прибыль от приложения с учетом требуемой работы. Этот полезный фактор следует учитывать, если проект начинает выпадать из временных рамок. Пример отбраковки требований:.
[важно] Все игровые персонажи имеют одинаковые наборы характеристик, [желательно] Каждая зона имеет набор предпочтительных характеристик, [не обязательно] Персонаж игрока должен взрослеть с каждой встречей. Скорость взросления может быть установлена во время начальных настроек. По умолчанию она равна одному году за каждую встречу.
Приведенный далее пример содержит некоторые приоритетные D-требования для первой версии игры Встреча. Они перечислены здесь для того, чтобы читатель смог себе представить, с вопросами какого типа ему придется иметь дело. Некоторые желательные требования в будущих версиях перейдут в категорию важных. Требования приведены в черновом варианте и, очевидно, нуждаются в повторном упорядочивании. Они будут исправлены дальше в тексте главы и в примере.
Предварительный черновик D-требований игры Встреча.
(Требования еще не организованы: см. исправленную форму в примере в конце главы.).
♦ (не проверено:)[важно] Каждый игровой персонаж в видеоигре Встреча должен иметь имя.
♦ (не проверено:)[важно] Каждый игровой персонаж имеет некоторый набор характеристик, значение каждой из которых представлено вещественным числом с дробной частью.
4- (не проверено:)[важно] Игра происходит в зонах, каждая из которых соединена с другими зонами посредством выходов.
4 (не проверено:)[важно] Каждый раз, когда персонаж игры входит в зону, в которой находится другой персонаж, и один из этих персонажей контролируется игроком, персонажи могут либо по выбору, либо принудительно вступать в контакт друг с другом.
♦ (не проверено:)[важно] Каждый раз, когда персонаж, контролируемый игроком, находится один в зоне, игрок может изменять значения его характеристик.
4 (не проверено:)[желательно] Имя каждого персонажа в игре может состоять не более чем из 15 символов.
♦ (не проверено:)[желательно] В любой момент персонаж будет иметь некоторое число очков-жизней, под которым понимается сумма значений его характеристик.
4 (не проверено:)[желательно] Каждая зона имеет набор предпочтительных характеристик.
4 (не проверено:)[желательно] Зоны сражений требуют силы и выносливости; зоны жилых комнат требуют внимательности и ума.
♦ (не проверено:)[желательно] Сумма значений характеристик игрового персонажа, соответствующих требованиям зоны, будет называться значением персонажа в зоне. Во время контакта система будет сравнивать значения персонажей в зоне и передавать сильнейшему половину очков-жизней слабейшего. Например, представьте себе, что игрок вступает в контакт с внешним персонажем в зоне, требующей выносливости и внимательности, и р
— значение выносливости игрока. Предположив, чтс
получим:
где штрихом отмечены значения после контакта.
4 (не проверено:) [не обязательно] При контакте расчет результатов будет происходить менее чем за секунду.
4 (не проверено:)[не обязательно] Персонаж игрока будет стареть с каждым контактом. Скорость взросления можно установить в начальных настройках. По умолчанию за каждый контакт добавляется год.
♦ (не проверено:)[не обязательно] Персонажи, управляемые игроком, теряют или набирают значения своих характеристик в конце каждого контакта со скоростью +2 % при значении возраста до 30 и -2 % при значении возраста больше 30.
Расстановка приоритетов требований связана с итерацией, реализующей их. Например, если мы не сможем реализовать необязательное требование.
При контакте расчет результатов будет происходить менее чем за секунду на второй итерации, оно может появиться на следующей итерации, но уже с более высоким приоритетом. Требования для каждой итерации хранятся в отдельном документе. Это помогает лучше понять более поздние требования.
4.3.4. Полнота.
Мы стараемся сделать каждое подробное требование самодостаточным, но на практике это редко удается, когда требования часто ссылаются на другие требования. Полнота набора требований гарантирует, что не было сделано никаких пропусков, подрывающих сформулированные требования. Ниже представлен неполный набор требований. Без указания того, как должно отображаться видео, этот набор требований является неполным. Начало требований.
1.
Приложение должно показывать фильм, название которого введено в строке приглашения.
2.
Приложение должно показывать все фильмы, фамилия режиссера которых введена в строке приглашения.
* Порядок показа определяется с помощью клавиши перемещения курсора Т.
3.
Приложение должно показывать все фильмы с участием актера, фамилия которого введена в строке приглашения.
* Порядок показа определяется с помощью клавиши перемещения курсора Т. Конец требований.
Опущено: Специфицировать, как «показать» видео!.
Изучая пример видеоигры Встреча, попробуем критически оценить, имеем ли мы полный набор требований. Вероятно, хорошим способом оценить текущий статус требований будет просмотр их с помощью вариантов использования.
♦ Мы устанавливаем характеристики персонажа игрока в гардеробной. Мы можем проверить наличие всей необходимой функциональности и данных для этого.
♦ Мы переходим в соседнюю зону. Проверяем наличие всей необходимой функциональности для этого.
♦ Мы обнаруживаем внешний персонаж. Удостоверяемся, что все детали для этого предоставлены.
4.3.5.
Состояние ошибки.
Для каждого требования нужно поставить вопрос: «Что будет, если произойдет ошибка?». Рассмотрим пример, предложенный Майерсом [82], — требование, которому не хватает обязательных условий ошибки.
Функция, определяющая, образуют ли три числа равносторонний, равнобедренный или разносторонний треуголгмик.
Спецификация этого требования неполная, поскольку она не рассматривает состояний ошибки. Следующая версия является более полной. Отсутствие рассмотрений ошибок в спецификациях требований становится особенно заметным при тестировании функций, поскольку тестер предвидит ошибки и должен знать, что должно выводиться в этом случае.
Функция, определяющая, образуют ли три числа:.
1 — равносторонний треугольник (со сторонами больше нут и равными), в этом случае выводится слово «равносторонний»,.
2 — равнобедренный треугольник (со сторонами больше нуля, ровно две из которых равны, и которые образуют треугольник), в этом случае система возвращает слово «равнобедренный»,.
3 — разносторонний треугольник (стороны которого все больше нуля и образуют треугольник, не являющийся ни равносторонним, ни равнобедренными), в этом случае система возвращает слово «разносторонний»,.
4 — треугольник не образуется, в этом случае система возвращает слово «нет».
Тщательный анализ требований имеет дело с неразрешенным вводом. Существует большой соблазн предположить, что пользовательский интерфейс для некоторого требования не разрешает ввод отрицательных значений, так что функции не придется работать с ошибочными данными. Вообще говоря, такое предположение неразумно, поскольку оно перекладывает требования корректности данных на пользователей. Это увеличивает зависимость между частями программы. Хотя хорошей практикой считается отслеживать некорректный ввод данных на уровне графического пользовательского интерфейса и не обязывать пользователя вводить только разрешенные значения, это не заменяет жестких требований в других местах. Автор рекомендует требовать отслеживания некорректных данных во многих, если не во всех, местах программы. Это один из эквивалентов давно сложившейся практики программирования, когда избыточность используется для повышения надежности.
4.3.6.
Согласованность.
Набор D-требований согласован, если между требованиями нет противоречий. По мере увеличения числа D-требований согласованность может стать труднодостижимой, что иллюстрирует следующий пример:.
Требование 14. Игровые персонажи могут нести только основные продукты питания.
Требование 223. Любой персонаж несет воду.
Требование 497. Только хлеб, масло, молоко и соль являются основными продуктами питания.
Объектно-ориентированная организация требований помогает избежать несогласованности благодаря классификации D-требований по классам и с помощью разложения их на простейшие. Однако и это не гарантирует согласованности, поэтому следует проверять согласованность вместе с другими рассмотренными характеристиками.
4.3.7. Подведение итогов процесса написания детальных требований.
Мы подводим итог процессу, которому можно следовать при выражении одного требования. Большинство шагов, вынесенных в отдельные пункты, были описаны в этом разделе как желательные характеристики требований.
Вот дополнительные замечания, соответствующие пронумерованным шагам во врезках «Один из способов...».
1.
В разделе 4.5 обсуждаются способы организации детальных требований с акцентом на объектно-ориентированный стиль. Метод организации должен быть определен до написания D-требований.
2.
Оценка того, является требование прослеживаемым или нет, эквивалентна представлению архитектуры программы и того способа, которым архитектура должна удовлетворять этому требованию. Проще всего это сделать, если требование четко соответствует методу.
ОДИН ИЗ СПОСОБОВ НАПИСАТЬ ДЕТАЛЬНОЕ ТРЕБОВАНИЕ -.
1.
Классифицируйте требование как функциональное или нефункциональное:.
♦ используйте подсказки IEEE SRS для большинства нефункциональных требований;.
♦ выберите метод организации функциональных требований.
2.
Аккуратно отсортируйте требования:.
♦ функциональное требование более-менее соответствует методу;.
♦ слишком большое — трудно управлять;.
♦ слишком маленькое — нет смысла рассматривать отдельно.
3.
По возможности сделайте его прослеживаемым. Убедитесь в возможности прослеживания при проектировании и реализации.
4 Сделайте его тестируемым. Набросайте конкретный тест, устанавливающий выполнение требования.
5.
Убедитесь в недвусмысленности требования. Убедитесь в очевидности замысла.
6.
Назначьте требованию приоритет. Например, высокий («важно»), низкий («не обязательно») или средний («желательно»),.
7.
Проверьте полноту требования. Для каждого требования убедитесь в присутствии всех остальных необходимых сопутствующих требований.
8.
Добавьте состояния ошибки:.
♦ сформулируйте, что конкретно требуется для нештатных ситуаций;.
♦ в критичных местах добавьте состояния ошибок программирования.
9.
Проверьте согласованность. Убедитесь, что ни одно требование не противоречит каким-либо аспектам другого требования.
3.
Проще наметить тест для требования во время написания самого требования. Это не только делает требование более понятным, но и определяет тестируемость требования.
4.
Многие требования зависят от конкретных данных, и мы должны указывать, как требование должно работать в случае неправильных или несогласованных данных. Для критичных требований сюда также следует относить ошибки неправильного проектирования или программирования. Например, допустимое требование: «когда нажата кнопка On, высокоинтенсивный рентгеновский луч будет включен, если параметры удовлетворяют условиям». Недопустимое требование: «позиции при игре в крестики-нолики должны отображаться при условии, что ни один игрок не сделал на два хода больше другого».
4.4. Диаграммы последовательности.
Диаграммы последовательности являются графическим представлением передачи управления и особенно полезны для визуализации реализации вариантов использования. Помимо использования их для анализа требований, как мы делаем в этой главе, мы также используем их более подробные версии для проектирования, как показано в главе 6.
Диаграммы последовательности заставляют нас рассуждать в терминах объектов. В такой диаграмме жизненный цикл каждого участвующего объекта показан вертикальной линией с именем объекта и указанием его класса вверху. Каждое взаимодействие между объектами отображается горизонтальной стрелкой от объекта, инициирующего взаимодействие, к объекту, выполняющему дальнейшие функции. Начало диаграммы последовательности для варианта использования Встречи показано на рис. 4.7. Далее приведены замечания к рис. 4.7.
1.
Прежде всего, мы обозначаем объект, инициирующий вариант использования, согласно UML с помощью прямоугольника. Это объект класса ИграВстреча. Тот факт, что упоминается не конкретный объект, а весь класс, указывает на то, что либо объект не требуется вообще (например, используются статические методы), либо объекта без какого-либо конкретного имени («анонимного» объекта) будет вполне достаточно.
2.
Узкий продолговатый прямоугольник обозначает выполнение функции объекта.
3.
Затем мы показываем выполнение операции объекта Зола, инициированного из ИграВстреча: в этом случае используется операция создания, которая обычно является конструктором. Созданный объект — объект гарде робнаяЗона. Неожиданно мы принимаем решение сделать Зону классом. (Имя, которое мы выбрали для этого класса, не совсем корректно — слишком обобщенно.) Нам следовало назвать его ЗонаВстреч; в дальнейшем мы проследим последствия этого дефекта.
4.
Обратите внимание, что жирная линия для объекта гардероб начинается лишь тогда, когда объект начинает существовать.
Рис. 4.7. Начало диаграммы последовательности для варианта использования «Инициализировать»
В законченной диаграмме последовательности (рис. 4.8) представлены операции. Эти операции показывают работу, инициируемую объектом с исходящей стрелкой и выполняемую объектом, на который указывает стрелка. Каждая операция обычно реализуется функцией на стадии проектирования. Цифрами на рис. 4.8 обозначены ссылки на варианты использования.
Рис. 4.8. Диаграмма последовательности для варианта использования «Инициализировать»
Полная диаграмма последовательности для варианта использования приведена на рис. 4.8. Идея состоит в том, что по мере того как вы проигрываете вариант использования, становится очевидной необходимость некоторых объектов. Эти объекты заставляют вас вводить новые классы. В варианте использования «Инициализировать» первое действие, предпринимаемое программой — показать гардероб и главный персонаж игрока. Нам нужен объект, который выполнял бы этот процесс создания. Разумным решением этой задачи будет один объект класса И/раВстреча. ИграВстреча создает объект гардероб класса Зона, затем «говорит» объекту гардероб показать себя. Каждый вытянутый вертикально прямоугольник показывает выполнение вызванной функции. Некоторые из пронумерованных шагов варианта использования реализуются вызовом более чем одной функции. Мы представили класс ПерсонажИгрока, которому принадлежит только один объект — главныйПерсонажИгрока.
На этот момент диаграмма последовательности служит для определения основных классов. Обычно диаграмму последовательности приходится менять и уточнять во время проектирования. Примером является определение класса, отвечающего за создание объекта гардероб. Архитектура (глава 5) может вызвать создание объекта гардероб не из объекта класса ИграВстреча, а из самого класса Зона либо еще из какого-нибудь другого класса.
Диаграммы последовательности могут использоваться для определения параллельных потоков управления. Например, мы можем захотеть, чтобы персонажи игры двигались независимо от зоны к зоне. В нотации UML для этого случая предусмотрена стрелка от одной колонки к другой, причем изображается только нижняя половина стрелки (рис. 4.9). Это означает инициализацию потока управления в конце стрелки.
Рис. 4.9. Диаграмма последовательности, показывающая параллелизм
Параллельные вертикальные полосы уместно использовать для визуализации параллелизма. Однако диаграммы последовательности неудобно использовать для описания синхронизации. Это можно сделать с помощью диаграмм деятельности UML [64]. За исключением случаев обязательного выражения требований заказчика, такие вопросы, как синхронизация, могут быть отложены до стадии проектирования.
Подведем итог шагов, необходимых для создания диаграммы последовательности. В случае, когда первым объектом является пользователь, вместо прямоугольника будет достаточно простой метки вверху. Отметьте, что объект, отвечающий за выполнение отмеченной на стрелке работы, стоит в конце (а не в начале) стрелки. Наиболее значимой частью процесса является выбор классов. Мы должны решить, какие общие виды элементов (объекты какого класса) будут выполнять работу, требуемую от приложения.
ОДИН ИЗ СПОСОБОВ ПОСТРОИТЬ ДИАГРАММУ ПОСЛЕДОВАТЕЛЬНОСТИ (1) -
ОДИН ИЗ СПОСОБОВ ПОСТРОИТЬ ДИАГРАММУ ПОСЛЕДОВАТЕЛЬНОСТИ (2)
4.5. Организация D-требований.
4.5.1. Почему так важно организовать детальные требования.
Чтобы понять значение аккуратно организованных D-требований, представьте себе следующую довольно беспорядочную попытку написания D-требований для игры Встреча. Обратите внимание, что эти требования все еще «сырые» и непроверенные.
♦ Каждый персонаж в видеоигре Встреча должен иметь имя. + Каждый игровой персонаж имеет одинаковый набор характеристик, значение каждой представлено вещественным числом.
♦ В игре Встреча на вычисление результатов контакта должно уходить не более одной секунды.
♦ Каждая зона должна иметь конкретный набор «необходимых характеристик». Например, зоны сражений требуют силы и выносливости; зоны жилых комнат требуют внимательности и ума.
♦ Когда два персонажа игры оказываются в одной зоне одновременно, они могут по выбору или принудительно вступить в контакт друг с другом.
♦ У каждого игрового персонажа должно быть некоторое число очков-жизней.
♦ Сумма значений характеристик игрового персонажа, соответствующих требованиям зоны, называется значением персонажа в зоне. Во время контакта система должна сравнивать значения персонажей в зоне и вычислять результат контакта.
♦ Имя каждого персонажа может содержать не более 15 символов.
Неорганизованный список, подобный приведенному выше, быстро превращается в неуправляемый по мере разрастания.
♦ Такой список трудно понять в целом, даже пока он не вырос до сотен, если не тысяч элементов.
♦ Требования имеют разные типы: например, требования к производительности должны обрабатываться иначе, чем требования поведения.
♦ Некоторые требования можно естественным образом объединить с другими.
♦ Трудно найти какое-либо конкретное требование.
4.5.2. Способы организации детальных требований.
D-требования можно организовать с помощью нескольких схем:.
♦ по основгсым свойствам (предоставляемый вовне сервис, обычно определяется с помощью пар стимул—реакция). Этот способ организации часто воспринимают как «требования», имея в виду, что требования сгруппированы по различным свойствам программы. Заметьте, что само по себе это не предоставляет никакой систематической организации, поскольку позволяет переходить от свойства одной части программы к свойству абсолютно другой части программы;.
♦ по режиму (например, системы управления радарами могут иметь тренировочный, нормальный и аварийный режимы);.
♦ по вариантам использования (иногда еще называется по сценариям). Эта организация будет подробно рассмотрена позднее. Идея заключается в том, что большинство детальных требований являются частью варианта использования;.
♦ по классу. Это объектно-ориентированный стиль, подробно объясненный далее. В этом способе организации мы классифицируем требования по классам. Такой способ организации использован в нашем примере;.
♦ по иерархии функций (то есть путем разбиения программы на множество высокоуровневых функций и последующего разбиения их на подфункции и т. д.) Например, требования для программы домашнего бюджета можно разбить на (1) функции проверки, (2) функции сбережений и (3) функции инвестирования. Функции проверки могут затем быть разложены на функции чековой книжки, баланса счета, составление отчетов и т. д. Это традиционный способ упорядочивания детальных требований;.
♦ по состояниям (то есть путем указания детальных требований, применимых к каждому состоянию). В частности, требования для программы, управляющей химическим процессом, лучше всего классифицировать по состояниям, в которых может находиться процесс (начало, реакция, охлаждение и т. д.). Внутри классификации каждого состояния перечислены события, влияющие на программу, находящуюся в конкретном состоянии.
Классификация по состояниям может быть уместна, если требования для каждого состояния сильно отличаются. Например, бухгалтерская система может вести себя по-разному в зависимости от ее состояний, таких как Конфигурация, Исполнение или Сохранение. Хотя требования примера игры Встреча можно организовать по состояниям, мы решили, что это было бы не так удобно, как их организация по классам.
Ниже приведены возможные способы организации D-требований:.
♦ по свойствам;.
♦ по вариантам использования;.
♦ по классам;.
♦ по иерархии функций;.
♦ по состояниям.
Следующие способы классификации D-требований взяты из стандарта IEEE 830-1993. Пользователи подобных схем могут добавлять разделы по необходимости. Например, в объектно-ориентированной классификации не хватает раздела, эквивалентного разделу 3.4 в не объектно-ориентированной классификации «Требования логических баз данных». Пример в конце главы использует модифицированную форму объектно-ориентированного стиля IEEE и включает раздел для вариантов использования.
IEEE 830-1993: Детальные требования ООП и других стилей.
3. Конкретные требования (не ОО-формат).
3.1.
Внешние интерфейсные требования.
3.2.
Функции.
3.3.
Требования к производительности.
3.4.
Логические требования базы данных.
3.5.
Ограничения проектирования 3.5.1. Соответствие стандартам.
3.6.
Атрибуты программной системы.
3.6.1.
Надежность.
3.6.2.
Доступность.
3.6.3.
Защищенность.
3.6.4.
Поддержка.
3.6.5.
Переносимость.
3.7.
Организация конкретных требований.
3.7.1.
Системный режим.
3.7.2.
Пользовательский класс.
3.7.3.
Объекты.
3.7.4.
Свойства.
3.7.5.
Входной сигнал.
3.7.6.
Ответный сигнал.
3.7.7.
Функциональная иерархия.
3.8.8.
Дополнительные комментарии 3. Конкретные требования (ОО-формат).
3.1.
Требования к внешнему интерфейсу.
3.1.1.
Пользовательские интерфейсы.
3.1.2.
Аппаратные интерфейсы.
3.1.3.
Программные интерфейсы.
3.1.4.
Коммуникационные интерфейсы.
3.2.
Классы/Объекты 3.2.1. Класс/Объект 1.
3.2.1.1.
Атрибуты (прямые или унаследованные) 3.2.1.1.1. Атрибут 1...
3.2.1.2.
Функции (службы, методы, прямые или унаследованные) 3.2.1.2.1. Функциональное требование...
3.3.
Требования к производительности.
3.4.
Ограничения проектирования.
3.5.
Атрибуты программной системы.
3.6.
Дополнительные требования.
Можно посоветовать организовывать детальные требования в комбинацию классификаций. Например, в состояниях Конфигурация, Исполнение или Сохранение бухгалтерской программы можно было бы использовать организацию по характерным свойствам. Требования для автоматизированной системы завода можно было бы организовать на самом высоком уровне по функциям (потребление, производство и сборка), а затем упорядочить по классам в каждой из получившихся функций.
Способ организации D-требований часто связан с возможной архитектурой программного приложения. Например, если проект будет объектно-ориентированным, должна использоваться организация по вариантам использования или по классам, поскольку это облегчит прослеживаемость. Это будет объяснено в следующих двух разделах. Если имеются действующие лица, осуществляющие все существующие и возможные требования отдельно, можно отдать предпочтение организации по действующим лицам.
4.5.3. Организация детальных требований по вариантам использования.
Унифицированный процесс разработки программного обеспечения (USDP) использует то наблюдение, что многие требования естественно встречаются в последовательностях операций. Например, требование того, чтобы программа видеомагазина позволяла вводить название нового фильма, имеет место как часть последовательности сделки. Это варианты использования, которые еще часто называют сценариями (в UML сценарием часто называют экземпляр варианта использования). Набор вариантов использования для программы видеомагазина проиллюстрирован на рис. 4.10.
Унифицированный процесс разработки программного обеспечения благоприятствует организации требований по вариантам использования. Если мы органи-
Рис. 4.10. Организация требований по вариантам использования: пример видеомагазина
зуем D-требования таким образом, имеет смысл разрабатывать более крупные варианты использования из маленьких. Способ создания таких вариантов использования с помощью отношения обобщения UML показан на рис. 4.11. Вариант использования В обобщает вариант использования D, если D содержит все шаги В (обычно также и дополнительные шаги).
Рис. 4.11. Расширения и обобщения варианта использования
Для дополнительных способов получения новых вариантов использования из существующих см. [95].
4.5.4. Организация требований по классам.
Мы сконцентрируемся на объектно-ориентированном стиле организации требований, в котором прежде всего надо определить классификацию — эквивалент выбора классов. Затем отдельные требования распределяют по получившимся категориям или классам. Для этого существует два подхода. Во-первых, можно рассматривать классы как способ организации требований, но не считать их обязательно используемыми в реальном проектировании. Во-вторых, мы можем использовать классы, разработанные для требований, в реальном объектно-ориентированном проектировании (и реализации), как мы и поступаем в нашем примере. Последний подход поддерживает взаимнооднозначную прослеживаемость между D-требованиями и методами, создавая соответствие между каждым функциональным D-требованием и функцией на целевом языке.
Один из недостатков этого подхода заключается в риске, связанном с тем, что позднее мы изменим используемые классы, нарушив соответствие между требованиями и классами [69]. Другой недостаток этой классификации в том, что здесь от нас требуется выбрать классы довольно рано в процессе разработки, причем многие считают, что мы таким образом эффективно выполняем проектирование. Рассмотрим в качестве примера игру Встреча. Выбор таких классов, как ПерсонажИгрока и Зона, на стадии требований не причинит вреда, поскольку эти классы скорее всего будут согласовываться с любой выбранной архитектурой. Иными словами, эти классы скорее всего и будут использованы в реализации. С другой стороны, поскольку объекты СоединениеЗоны ссылаются на объекты соответствующих Зон, это может быть рассмотрено как решение проектирования.
Большим преимуществом организации требований по классам, которые будут использованы в проектировании, является поддержка жесткого соответствия между требованиями, проектом и реализацией. Это главное достоинство использования объектно-ориентированного принципа. Вдобавок для классов, соответствующих понятиям реального мира, гораздо больше вероятность повторного использования. Для многих программ выгода от использования метода организации требований по классам перевешивает все недостатки.
Типичная последовательность получения функциональных D-требований с использованием объектно-ориентированного стиля приведена ниже.
1.
Процесс начинается с перечисления классов, упомянутых в вариантах использования.
2.
Полученный набор классов обычно неполон, и следует попытаться найти остальные классы предметной области. Этот процесс описан ниже.
3.
Для каждого из полученных классов выписывается вся необходимая функциональность программы, за которую отвечает данный класс. Это показано в примере в конце главы. Документ создается в форме атрибутов и функций. Например, «у каждого клиента будет имя» (атрибут класса Клиент) и «программа должна иметь возможность вычислять капитал каждого клиента» (функция класса Клиент).
Каждый известный обязательный объект класса должен быть отдельно указан как требование к классу. Например, «Концерн семьи Рокфеллер должен быть клиентом».
События, которые должны обрабатывать объекты класса, должны быть также определены.
D-требования проверяются по ходу процесса.
В идеале в это же время должны быть разработаны и планы тестирования для каждого D-требования, как будет объяснено далее.
4.
D-требования затем проверяются и сравниваются с С-требованиями.
5.
D-требования проверяются заказчиком, и лишь затем публикуются. Вспомните, что основной аудиторией для D-требований являются разработчики. Однако заказчики также существенно заинтересованы в деталях.
Итог этих шагов подводится на рис. 4.12.
Рис. 4.12. Схема D-требований с использованием объектно-ориентированного стиля
Частой ошибкой, возникающей при организации требований по классам, является рассмотрение процесса определения D-требований как процесса проектирования. Для описания детальных требований должен использоваться простой язык. Например, следующий язык приемлем:.
Должна существовать возможность получить число неоплаченных дней на любом счету.
Нижеследующая фраза неприемлема:.
getDelinquentDays() возвращает количество неоплаченных дней на счету.
Другими словами, объектно-ориентированный подход используется только как принцип организации требований. Использование его для проектирования и реализации выполняется позднее.
4.5.5. Определение классов.
Классы, которые будут использоваться в качестве нашего организующего принципа, должны определяться аккуратно и надежно. Это достигается путем определения классов предметной области, принадлежащих программе. Например, среди классов предметной области банковской программы могут быть классы КлиентБанка и Кассир, но не Файл или БазаДанных и даже не Клиент или Операция. Последние два класса не являются конкретными для нашего приложения. Использование классов предметной области является способом организации, обдумывания и отслеживания требований. Наша цель заключается в определении минимального, но достаточного набора классов предметной области, включающих все детальные требования.
В качестве другого примера набора классов предметной области представим программу, управляющую посещениями веб-сайта. Кандидатами в классы будут ПосетительСайта, ПосещениеСайта и ЦельПосещения. Требования, относящиеся к посетителю (например, данные о посетителях, показ профиля посетителя), будут собираться с помощью класса ПосетительСайта. Если программе требуется, чтобы мы отслеживали причины каждого посещения, класс ЦельПосещения также будет уместен. Соответствующие требования будут собраны внутри класса ЦельПосещения. Например, в требования к программе будет входить требование, согласно которому посетители должны заполнять форму с указанием их целей при посещении сайта.
Варианты использования являются основным источником классов предметной области. Вспомните диаграмму последовательности для примера игры Встреча (см. рис. 4.8).
За исключением случая, когда программа представляет собой преимущественно графический пользовательский интерфейс, автор советует по возможности отложить рассмотрение классов графического пользовательского интерфейса до стадии проектирования. Причина состоит в том, что это не основные классы предметной области и разработчики часто их изменяют. Для первых версий можно попробовать использовать интерфейс командной строки. С другой стороны, классы графического пользовательского интерфейса могут потребоваться рано, поскольку клиент просто хочет увидеть какую-то графику на ранних этапах. В примере нам пришлось рано добавить класс ОкноХарактеристикИгрока, поскольку он упоминается в варианте использования С-требования с тем же именем. Вспомните, что диаграмма последовательности использует следующие классы:.
+ ИграВстреча.
класс, имеющий один объект;.
♦ персонажИгрока.
с объектом главпыйПерсонажИгрока;.
♦ зона.
с объектом Гардероб;.
♦ окноХарактеристикИгрока.
класс графического интерфейса пользователя, добавленный для полноты варианта использования.
Классы ГинерссылкаСоединения, СоединениеЗоны, Зона и ПерсонажИгрока обязательны для диаграммы последовательности «Перейти в соседнюю зону» (рис. 4.13), которая соответствует одноименному варианту использования в С-требовании. Классы ВнешнийПерсонаж, Контакт и ИзображениеКоитакта вызываются в диаграмме последовательности «Вступить в контакт с внешним персонажем» (рис. 4.14). Нумерация в диаграмме последовательности показывает создание, а затем исполнение события Контакт для обработки результатов контакта. Это событие изменяет значения участников (шаг 2.1) в соответствии с правилами контакта. Затем контакт вызывает свою операцию отображения результатов (шаг 3.1) и т. д.
Рис. 4.13. Диаграмма последовательности для варианта использования «Перейти в соседнюю зону»
Рис. 4.14. Диаграмма последовательности для варианта использования «Вступить в контакт с внешним персонажем»
После получения классов из вариантов использования эффективным способом завершения определения ключевых классов предметной области является использование процесса «перечислить и урезать». Он состоит, во-первых, из перечисления всех возможных классов-кандидатов, которые вы только можете придумать, и во-вторых, из агрессивного удаления лишних кандидатов, после которого должны остаться лишь несколько самых важных.
1.
Классы-кандидаты для игры Встреча, отобранные из диаграмм последовательности, вместе с существительными из предшествующего описания игры в этой главе показаны на рис. 4.15. UML-нотация для класса — это прямоугольник с именем класса.
2.
Затем мы их фильтруем. Сначала обратите внимание, что гораздо проще добавить класс в дальнейшем, чем удалить класс, встроенный в проектирование и реализацию, так что если вы сомневаетесь относительно полезности класса-кандидата, смело удаляйте его.
Логика, использованная для конечного выбора классов предметной области в примере, приводится далее.
♦ Встреча. Изменить на ИграВстреча, чтобы подчеркнуть цель (возможно, нам еще понадобится и понятие «встреча»).
♦ Игра: не класс предметной области — слишком общий (возможно, мы повторно введем его, когда будем искать подходящие обобщения).
♦ ПерсоиажИгры: слишком общий, чтобы быть классом предметной области (возможно, мы повторно введем его, когда будем искать подходящие обобщения).
♦ Игрок: более предпочтительно имя ПерсонажИгрока (более конкретно для класса).
♦ ВнешнийПерсонаж: ОК (внешние персонажи действуют иначе, чем персонаж игрока).
♦ ПерсонажВстречи: ОК (обобщение ПерсонажаИгрока, ВнешнегоПерсонажа и т. д. — все еще внутри предметной области программы).
♦ Характеристика: пропустить — попытайтесь обработать как простой атрибут класса ПерсонажВстречи.
♦ Комната: пропустить — не уверены, нужно ли это; уже есть Зона.
♦ Дверь: пропустить — не уверены, понадобится ли это.
♦ Выход: не уверены, понадобится ли: ведет в соседнюю зону — попробуйте как простой атрибут Зоны. Пока пропустить.
♦ Правило: пропустить — не уверены, понадобится ли это.
♦ Зона: ОК [внимательный читатель заметит, что это дефектное решение].
+ Контакт: ОК.
♦ Проход: Нам нужно соединять зоны, но мы еще не знаем, в какой форме будут реализованы эти соединения. Вместо этого используйте СоединениеЗоныВстречи.
♦ Результат, пропустить — не ясно.
+ Сражение: пропустить — не уверены, нужно ли это: уже есть Контакт.
♦ Очки: пропустить — попробуйте как атрибут других классов.
♦ ОкноХарактеристикИгрока: необходимо для выражения варианта использования «Инициализировать».
♦ ОкноВыбораВыхода: пропустить — не нужно (достаточно щелкнуть на гиперссылке выхода).
♦ Карта: пропустить — не обязательно на этом этапе: возможно, появится в будущих версиях.
♦ ОкноКонтакта: ОК — нужно для варианта использования, хотя мы постараемся отложить это, заменив все интерфейсом командной строки.
Рис. 4.15. Классы-кандидаты для игры Встреча
Получившиеся классы показаны на рис. 4.16. На рисунке также показаны наследственные связи между этими классами, отмеченные стрелкой с треугольником.
Существуют и другие способы наследования, в которых классы на рис. 4.16 потенциально связаны. Например, СоединениеЗоныВстречи, скорее всего, будет агрегировать два объекта класса Зона. Здесь наше опасение касается лишь основных классов программы, а также использования их для организации требований. Связи между классами показаны, где это необходимо. Использование наследования обеспечивает определенную свободу. Например, после утверждения требований для класса ПерсонажВстречи нам не нужно повторять требования для описания классов ПерсонажИгрока и ВнешнийПерсонаж. Класс ПерсонажВстречи показан курсивом на рис. 4.16, поскольку он абстрактный: это означает, что не будет других персонажей, помимо персонажей, управляемых игроком, или внешних.
Рис. 4.16. Классы для видеоигры Встреча с отмеченным наследованием
Описанный выше метод идентификации классов для классификации детальных требований показан в следующей врезке.
ОДИН ИЗ СПОСОБОВ ВЫБРАТЬ КЛАССЫ ПРЕДМЕТНОЙ ОБЛАСТИ -.
ДЛЯ КЛАССИФИКАЦИИ ТРЕБОВАНИЙ.
1.
Разработать полный набор непересекающихся вариантов использования.
2.
Создать диаграмму последовательности для каждого варианта использования. Не забыть отождествить классы и объекты.
3.
Собрать классы, использовавшиеся в диаграммах последовательности.
4.
Определить важные дополнительные классы.
5.
Распределить подробные функциональные требования по этим классам.
1) Указать каждый атрибут как отдельное требование.
2) Указать каждый конкретный объект этого класса, который должен существовать.
3) Указать каждую функцию, необходимую для объектов в этой классификации.
4) Перечислить события, на которые должны реагировать все объекты этого класса.
Стандарт IEEE 830-1993 SRS предписывает обозначать место, в котором сформулирована цель каждого класса, а также ключевые атрибуты и функции. Стандарт призывает использовать десятичную систему нумерации для каждого класса (например, 3.2.5), требований атрибутов (например, 3.2.5.1) и каждого функционального требования (например, 3.2.5.2). Вместо этого мы будем организовывать классы по алфавиту, чтобы упростить задачи добавления и удаления классов. Такие изменения будут необходимы по ходу роста программы. Кроме того, важно нумеровать требования, чтобы иметь возможность управлять ими, так что мы сделаем это внутри каждого класса, как показано в примере.
Иногда нумеруют только важные требования, поскольку на этот момент следует отслеживать только их. Этот стиль организации детальных требований проиллюстрирован в [69]. Для облегчения отслеживания D-требований может помочь нумерация каждого из них, как это сделано в примере. Например:.
З.2.А.7. Предпочтительные характеристики.
[важно] У каждой зоны будет предпочтительный набор характеристик.
Добавление желательных и необязательных детальных требований выгодно по нескольким причинам. Во-первых, границы программы можно контролировать путем реализации требований в запланированном порядке. Во-вторых, формулировка будущих требований направляет разработчиков, помогая им принимать проектные решения, которые смогут удовлетворить будущие потребности.
Одной из техник указания требований, для которых уже проведены проектирование и реализация, является добавление статуса к каждому из требований. Например:.
3.2.А. 7. Предпочтительные характеристики.
[важно, еще не реализовано] Каждая зона будет представима в одном из трех разных стилей.
Когда требование будет реализовано, фразу «еще не реализовано» можно убрать.
4.5.6.
Правильный выбор класса для данного требования.
Возможно, наиболее сложным вопросом при организации D-требований по классам является решение, какому классу приписать данное требование. Вот простой пример.
Требование. Каждый персонаж Встречи будет иметь имя.
Это требование должно быть отнесено к классу ПерсонажВстречи. А вот более сложный пример.
Требование. Каждый раз, когда главный персонаж игрока вступает в зону, эта зона и все персонажи, находящиеся в ней, будут показаны на мониторе.
Очевидно, классами-кандидатами для этой функции будут ПерсонажИгрока и Зона. Требование, очевидно, вызывает обработчик событий. Согласно интерфейсу, показанному в главе 3, персонаж игрока вступает в зону после того, как игрок щелкает на гиперссылке зоны. Поэтому естественным объектом для обработки события входа в зону будет зона, в которую вошел игрок, поскольку она должна владеть информацией относительно находящихся в ней персонажей. Зона, в которую вошел игрок, может показать себя и персонажи, которые находятся в ней. Поэтому требование, сформулированное выше, логично отнести к классу Зона.
4.5.7.
Классификация объектов.
Для программ необходимо присутствие конкретных объектов или экземпляров (а не классов). Например, наша программа с видеоигрой требует существования объектов двор и гардероб класса Зона. Где мы должны сформулировать эти требования? Существует по крайней мере три варианта. В первом варианте мы должны внести эти требования в класс, создающий объекты. Согласно диаграмме последовательности, ИграВстреча создает гардероб. Преимущество этого способа заключается в том, что такое решение отражается в коде, поскольку функция создания в объекте ИграВстреча будет прямо ссылаться на объект гардероб. Недостаток такого подхода заключается в том, что решение относительно того, какой объект будет участвовать в создании нашего объекта, с большой вероятностью изменится, и тогда требования также придется перемещать. Также следует отметить, что другие объекты, хотя и не создают наш объект, также могут ссылаться на него чаще, чем создающий его объект.
Второй вариант заключается во введении дополнительного класса, агрегирующего конкретные объекты каждого важного класса (например, класс Зоны, агрегирующий объекты класса Зона). Это может оказаться неудобным, и в этом случае также могут быть добавлены классы, не являющиеся необходимыми. Рассмотрим третий способ: перечисление требований к объектам и классу, которому они принадлежат.Требования существования объектов гардероб и двор могут быть перечислены для класса Зона следующим образом:.
Класс Зона.
Все действия игры (в том числе и контакты) происходят в Зонах.
Требование Зоны (гардероб). Должен существовать объект класса Зона, названный «гардероб». Он будет выглядеть... Его предпочтительными характеристиками должны быть сосредоточенность и выносливость.
Требование Зоны (двор). Должен существовать объект класса Зона, названный «двор». Он будет выглядеть... Его предпочтительной характеристикой должна быть сила.
4.5.8. Связь с документацией тестов.
По ходу классификации каждого D-требования следует выполнять некоторую работу по тестам для этого конкретного требования. Существует несколько преимуществ в написании тестов одновременно с требованием. Во-первых, такие действия помогают прояснить требование. Во-вторых, это переносит некоторую часть работы из фазы тестирования проекта в фазу требований. Это снимает некоторое давление на более поздних этапах проекта, когда временные рамки более ограничены.
Например, одно из требований выглядит так:.
Требование NNN. Каждый игровой персонаж в видеоигре Встреча будет иметь уникальное имя, содержащее от 1 до 15 символов.
Требования с таким типом атрибута на самом деле определяются функциями get- и set-. Начало тестовых планов для этого требования показано в табл. 4.2. Подробно эти тесты описаны в главе 8.
Таблица 4.2. Входные тестовые данные и ожидаемый результат
Входные тестовые данные для требования NNN
Ожидаемый результат
Гарри
Гарри
X
X
« » (пусто)
« » (пусто)
123456789012345
123456789012345
1234567890123456
123456789012345
Напомним (см. главу 1), что параллельное тестирование является важной характеристикой инкрементальной разработки и экстремального программирования. На основе концепции параллельного тестирования была построена методология, называемая методологией Y. Она получила свое название в связи с тем, что ее диаграмма представляет собой водопад, начинающийся слева, с соответствующей последовательностью фаз тестирования справа, тем самым образуя «рукава» буквы «Y». Эти два потока сливаются вместе на этапе интеграции, после чего происходит тестирование системы (глава 9).
Детали_.
Эту часть главы можно изучать после прочтения последующих глав. Однако понимание материалов, изложенных в этой части, обязательно для разработки качественного программного продукта.
4.6. Качество детальных требований.
Постоянно держа в голове потенциально возможные последствия неудач с формулированием деталей каждого требования (например, потерянный искусственный спутник), мы стараемся оценить качество как можно большего числа наших требований.
4.6.1. Роль контроля качества в анализе D-требований.
Организация, контролирующая качество, просматривает D-требования. Например, в стандарте планирования контроля качества IEEE (730.1-1995) сказано, что план контроля качества программного продукта.
«должен определять или ссылаться на стандартные практики, соглашения и метрики, которые в свою очередь должны использоваться на этапе определения требований. Должны быть указаны привлеченные стандарты..., которые должны быть выполнены в основных требованиях, и должна быть обеспечена их прослеживаемость. Формальные языки описания требований должны использоваться, где это возможно. Схемы, однозначно определяющие каждое требование, должны иметь основу...».
В стандарте подробно изложены форма и природа программных требований (раздел 3.6.2.1 IEEE 730.1-1995).
В больших проектах иногда следуют указаниям такого типа. Однако во многих средних и небольших организациях контроль качества вынесен в отдельный процесс, который начинает выполняться только после того, как все требования были определены. Иногда группу проверки качества даже просят по факту «проверить то, что вот это место было построено согласно спецификации». Типичной жалобой группы контроля качества является отсутствие адекватных требований, по которым можно было бы проверить работу программы. Иногда специалистам по контролю качества приходится создавать требования согласно самой программе и тому, как она была построена. Этот процесс называется обратным проектированием (глава 10). Однако ситуация меняется к лучшему по мере того, как организации-разработчики начинают относиться к процессу более серьезно.
4.6.2. Метрики для анализа D-требований.
Выборочное использование метрик максимизирует отдачу от материальных вложений в проверку, фокусируя процесс проверки на важных, измеренных результатах.
Каждая метрика дает выгоду, но стоит денег и времени для сбора, хранения, анализа и составления отчета. Искусство использования метрик заключается в оптимизации отношения затрат к получаемой выгоде. Это зависит от традиций организации, состояния проекта, природы проекта и многих других факторов. Сомнительной практикой является сбор метрик просто потому, что они могут пригодиться в будущем. Автор наблюдал огромные количества собранных данных, покрытых толстым слоем пыли, и все только потому, что кто-то посчитал, что они могут пригодиться, хотя и не сформулировал, как. Процесс сортировки для отбраковки метрик является полезной практикой, классифицирующей их на «обязательные» метрики, те метрики, которые «хорошо было бы иметь» и «остальные». Несколько попыток использования «остальных» метрик прояснят их стоимость и прибыль.
Далее приведен список метрик контроля качества, куда включены метрики анализа требований из стандарта IEEE 982.2-1988.
Метрики контроля качества детальных требований включают в себя: + метрики того, насколько хорошо написаны требования:.
♦ процент однозначных детальных требований (IEEE-метрика 6);.
♦ степень законченности (полнота) (IEEE-метрики 23 и 35);.
♦ процент неочевидных D-требований (в объектно-ориентированном стиле это измеряется процентом требований, размещенных в неправильном классе);.
♦ процент требований, которые:.
♦ не тестируются;.
♦ не прослеживаются (IEEE-метрика 7);.
♦ не отсортированы по приоритетам;.
♦ не элементарны (можно разбить на части);.
♦ не согласуются с остальными требованиями (IEEE-метрики 12 и 23); ♦ метрики эффективности проверки требований:.
♦ процент пропущенных или дефектных требований, найденных за каждый час проверки;.
♦ метрики эффективности процесса анализа требований:.
♦ стоимость каждого D-требования:.
• общая (общее затраченное время/число D-требований);.
• критическая (стоимость получить еще одно);.
• скорость, с которой детальные требования могут быть.
изменены; удалены; . добавлены;.
♦ метрики полноты требований:.
♦ можно оценить после официального завершения сбора D-требованип исходя из скорости, с которой требования.
• изменяют;.
• добавляют.
Метрики полезны, когда их целевые значения определены заранее. Например, базируясь на опыте предыдущих проектов, мы скажем, что требования будут считаться законченными, когда скорость внесения изменений и добавлений будет меньше 1 % в неделю.
4.6.3. Инспектирование анализа D-требований.
Читателю предлагается обратиться к главе 1 для общего определения процесса инспектирования.
Детальные требования (или D-требования) представляют собой первые документы процесса разработки, которые можно изучать, сравнивая с предшествующей документацией (С-требованиями). Инспекторы готовятся к проверке, перечитывают С-требования (например, IEEE 830-1993, разделы 1 и 2), и сравнивают с ними детальные требования.
4.6.3.1. Пример непроверенных D-требований.
В этом разделе приведена версия D-требований, для которой мы выполним пример проверки, вводя результаты в таблицу (см. табл. 4.3). Окончательная версия этих требований, получившихся после проверки, показана в примере в конце главы.
Зона. Требование 1 (название Зоны). [Еще не проверено] У каждой зоны должно быть имя, содержащее от 1 до 15 символов.
Зона. Требование 2 (изображение Зоны). [Еще не проверено] Должен быть рисунок в формате GIF, показывающий объект Зона.
Зона. Требование 3 (метод Показать). [Еще не проверено] Каждый раз, когда персонаж игрока входит в зону, эта зона и все персонажи, находящиеся в ней. должны быть показаны.
Зона. Требование 4 (объект двор). [Еще не проверено] Должен быть объект класса Зона с именем «двор». Его вид показан на рис. 4.38.
Зона. Требование 5 (объект гардероб). [Еще не проверено] Должен быть объект класса Зона с именем «гардероб» и пустым изображением на заднем плане. Гардероб будет соседней зоной для двора.
Встреча. Требование 1. (Вступить в контакт с знешним персонажем) [Еще не проверено] Каждый раз при контакте должны производиться следующие вычисления: сумма значений характеристик игрового персонажа по отношению к зоне будет называться значением в зоне [в этой версии все значения будем считать одинаковыми]. При контакте система сравнивает значения персонажей в зоне и передает сильнейшему половину значения слабого. Например, предположим, что игрок вступает в контакт с внешним игроком в зоне, требующей выносливости и внимательности, и ps — это значение выносливости игрока и т. д. Считая, что
получим
где штрихом отмечены новые значения.
ПерсонажВстречи. Требование 1 (Имя игрового персонажа). [Еще не проверено] Каждый игровой персонаж в игре Встреча будет иметь уникальное имя, содержащее от 1 до 15 символов.
ПерсонажВстречи. Требование 2 (Характеристики игровых персонажей). [Еще не проверено] У каждого игрового персонажа имеется одинаковый набор характеристик, значение каждой из которых представлено вещественным числом. Изначально все значения равны 100/п, где п — число различных характеристик. Характеристиками являются внимательность, сосредоточенность, ум, терпение и сила.
ПерсонажВстречи. Требование 3 (Внешний вид игрового персонажа). [Еще не проверено] Каждый игровой персонаж будет показан с помощью рисунка, занимающего не более 1 /8 части экрана монитора.
ПерсонажВстречи. Требование 4 (Контакт с внешним персонажем). [Еще не проверено] Каждый раз, когда персонаж вступает в зону, содержащую другой игровой персонаж, и один из персонажей управляется игроком, персонаж игрока может либо по желанию, либо принудительно вступить в контакт с другим персонажем. Есть ли выбор, будет определяться игрой случайным образом с вероятностью 50 %.
ИграВстреча. Требование 1 (Встретить игровой объект) [Еще не проверено] Будет существовать только один объект класса ИграВстреча.
ВнешнийПерсонаяс. Требование 1 (Объект «Фредди», внешний персонаж). [Еще не проверено] Будет существовать внешний персонаж с именем Фредди, все значения характеристик которого равны и внешний вид которого показан на рис. 4.28.
ПерсонажИгрока. Требование 1 (Возможность настройки). [Еще не проверено] Когда в зоне нет внешних персонажей, игрок может устанавливать значения характеристик с помощью объекта ОкноХарактеристикИгрока, сохраняя сумму значений характеристик неизменной.
ПерсонажИгрока. Требование 2 (Главный персонаж игрока). [Еще не проверено] У игрока будет полный контроль над конкретным игровым персонажем, называемым главным персонажем.
ПерсонажИгрока. Требование 3 (Очки-жизни). [Еще не проверено] Игра будет рассчитывать сумму значений характеристик персонажа, которая будет называться количеством очков-жизней.
4.6.3.2. Пример результатов проверки D-требований.
В этом разделе мы покажем типичные результаты проверки D-требований.
Можно сделать одно замечание относительно этого набора в целом: требования недостаточно поддерживают преобразование игры в конкурентоспособный продукт. Более конкретным дефектом является то, что требования не определяют должным образом задержку, связанную с установкой значений характеристик игрока. Во время этой задержки игрок может быть вовлечен в контакт в неподготовленном состоянии. (Если задержка слишком мала, игрок просто устанавливает необходимые в зоне характеристики по максимуму, и игра не представляет особого интереса.) Давайте исследуем требования из предложенного списка по отдельности.
Пример формы, которую можно использовать для проверки D-требований применительно к созданному выше списку, приведен в табл. 4.3. Указанные в табл. 4.3 свойства определены ранее в разделе 4.3 (согласно [92]). Большинство метрик, описанных в этом разделе, можно рассчитать согласно этой таблице.
Вот примечания, отмеченные в таблице как «Прим.»:.
1.
Может ли персонаж или зона иметь имя без символов вообще?.
2.
Число 15 жестко закреплено.
3.
Только один?.
4.
Если игрок управляет несколькими персонажами, должны ли показываться все зоны, или это касается только главного персонажа?.
5.
Заполнение всего экрана монитора?.
6.
Должно быть проще добавлять новые характеристики или удалять их.
7.
Когда появляется Фредди?.
8.
В будущих версиях персонажи могут видоизменяться.
9.
Проясните, что остается без изменения.
10.
Может ли значение характеристики быть отрицательным?.
11.
Двусмысленность, поскольку игрок не может управлять всем, что происходит с главным персонажем, постоянно.
Уточните термин «полное управление».
1.
Разрешен ли абсолютно любой символ клавиатуры?.
2.
Проверить справедливость с заказчиком.
3.
Неясно, насколько изменяемым это должно быть.
4.
Трудно ответить «полный», поскольку это нечетко. См. ссылки на примечания в столбце «Ясность».
5.
Мы предполагаем, что у заказчика есть некоторая дополнительная информация относительно того, как должен выглядеть двор.
6.
А есть ли выходы из гардероба в другие зоны?.
7.
Нечетко написано: может привести к неправильному пониманию.
Таблица 4.3. Пример проверки результатов D-требований
Требование
Прослежи-.
ваемость.
назад
Полнота
Согласованность
Выполнимость
Однозначность
Ясность
Точность
Модифицируемость
Тестируемость
Прослежи-.
еаемость.
вперед
Зона
Прим. 14
Прим.
13
Да
Да
Прим.
1
Да
Прим.
1
Прим. 2
Прим. 1, 2
Да
Требование 1
Зона
Да
Да
Да
Да
Прим.
3
Да
Прим.
3
Прим. 15
Да
Да
Требование 2
Зона
Да
Прим.
17
Прим. 17
Да
Прим.
3
Прим.
3
Прим.
5
Да
Да
Да
Требование 5
Зона
Да
Да
Да
Да
Да
Да
Прим.
18
Прим. 15
Да
Да
Требование 3
Зона
Да
Прим.
19
Да
Да
Да
Да
Да
Да
Да
Да
Требование 4
Контакт
Прим. 14
Да
Да
Да
Да
Да
Да
Прим. 15
Да
Да
Требование 1
ПерсонажВстречи
Да
Прим.
13
Да
Да
Прим.
1
Да
Прим.
1
Прим. 2
Прим. 1, 2
Да
Требование 1
ПерсонажВстречи
Да
Да
Да
Да
Да
Прим.
20
Да
Прим. 6
Да
Прим. 21
Требование 2
ПерсонажВстречи
Да
Да
Да
Да
Да
Да
Да
Прим. 15
Да
Да
Требование 3
ПерсонажВстречи
Прим. 23
Да
Да
Да
Да
Да
Да
Прим. 7
Да
Да
Требование 4
ИграВстреча
Да
Да
Да
Да
Да
Да
Да
Прим.24
Да
Д
Требование 1
ВнешнийПерсонаж
Прим.25
Да
Да
Да
Да
Да
Прим.
8, 9
Да
Да
Да
Требование 1
ПерсонажИгрока
Да
Да
Да
Да
Да
Прим.
10
Да
Да
Да
Да
Требование 1
ПерсонажИгрока
Да
Да
Да
Да
Прим. 11
Прим.
12
Прим.
12
Прим. 15
Прим. 12
Да
Требование 2
ПерсонажИгрока
Да
Да
Да
Да
Прим.
22
Прим.
22
Да
Да
Да
Да
Требование 3
8.
Обычно предпочтительнее, чтобы каждому атрибуту соответствовало отдельное требование. Это не обязательно, поскольку разные характеристики можно обрабатывать одинаково.
9.
Рассчитывать в любое время? По требованию? Показывать всегда?.
10. Подробное! и не упомянуты в С-требованиях: проверить с заказчиком.
И. Если можно, объясните термин «с вероятностью 50 %».
12.
Для интернет-версий может быть необходимо иметь более одного объекта ИграВстреча. Мы не будем исключать такую возможность на будущих итерациях.
13.
Неясно, как это можно изменить.
14.
Написано ли требование с учетом того, что его можно будет проследить в коде, его реализующем?.
Вдобавок к этим пунктам IEEE определяет метрику полноты. Как описано в стандарте 982.2-19888 А35.1, это формула, затрагивающая 18 наблюдаемых величин (например, «число параметров условия без обработки») и 10 весовых коэффициентов (например, относительная важность «использования определенной функции»). Попросту говоря, она вычисляет степень наличия «дыр» в наборе D-требований.
4.7. Использование инструментов для анализа требований.
Инструментальные средства могут упростить процесс формирования требований и управления ими, например с помощью сортировки, расстановки приоритетов, назначения и прослеживания. Одной из выгод от использования инструментальных средств является информация о том, кто и когда работал над каким требованием. Инструментальные средства также помогают управлять «возникновением лишних свойств» — процессом появления в программе свойств, не являющихся жизненно важными. С помощью подходящих инструментов руководитель проекта может с большей легкостью определить статус анализа требований. Он может определить, например, какая часть важных D-требований была реализована и полностью протестирована группой контроля качества.
В простых проектах большинство этих операций можно выполнить, используя простую электронную таблицу, доступную через Интернет (табл. 4.4). Надпись «Спроектировано для» указывает, что требование учтено в проекте. «Модуль протестирован» означает, что код, реализующий данное требование, прошел модульное тестирование. «Сборка протестирована» означает, что программа была протестирована на предмет реализации требования.
Таблица хранится как часть документа статуса проекта. Ячейки этой таблицы можно связать посредством гиперссылок с соответствующими частями документов проекта.
Таблица 4.4. Пример таблицы для учета требований
Требование №
Приоритет
Статус
Степень готовности
Готово для инспектирования
Спроектировано для
Модуль протестирован
Сборка протестирована
Важно
Желательно
Необязательно
Не начато
1/3
2/3
Проинспектировано
Ответственный разработчик
Гиперссылки можно использовать для поддержки централизованной разработки D-требований (например, чтобы застраховаться от повторений). Так, гиперссылки из исходного кода на соответствующее D-требование можно создать с помощью таких инструментов, как Javadoc. Javadoc конвертирует некоторые комментарии к Java-коду в HTML-документ, описывающий классы и их методы (см., в частности, [102]). Благодаря вставке гиперссылок на SRS внутри таких комментариев, HTML-документ, созданный с помощью Javadoc, также имеет гиперссылки на SRS. Это иллюстрирует представленный ниже пример, где на детальное требование, соответствующее методу EngagingForeignCharacterQ (Вступить в контакт с внешним персонажем), идет ссылка из документа, сгенерированного Javadoc из исходного кода: /**.
Engagement Requirement 1 («Вступить в контакт с внешним персонажем»).
.
...Комментарии по реализации... */.
Цель этого метода дана в SRS. Цель не повторяется в исходном коде.
public engageForeignCharacter (...) {.
}.
4.8. Формальные методы для спецификации требований.
4.8.1. Введение в формальные спецификации.
Математика хороша для выражения статического состояния, другими словами, для ответа на вопрос «что?». Это отличается от вопросов типа «как?», на которые отвечают процедуры и алгоритмы. Поскольку спецификации требований в основном описывают состояние программы до и после действий, математическая нотация может быть более подходящей, чем естественный язык, для определения детальных требований. Использование математики в этом контексте является частью так называемых формальных методов. Формальные методы удобны для обученных математике инженеров. Этот раздел имеет своей целью познакомить читателя с идеей формальных методов для формулирования требований. Полное описание формальных методов спецификации вы найдете в [42] и [36].
Многие ученые верят, что математика играет важную роль в определении всех важных деталей, которые так легко могут испортить самый продуманный проект. Они считают, что такое использование математики может предотвратить пустую трату денег, разрушение имущества и даже человеческие потери (например, в жизненно важных программах).
Как пример рассмотрим следующее определение процедуры:.
Возвращает отсортированный массив, состоящий из элементов массива А.
Это простое на первый взгляд требование поразительно наполнено неопределенностями. Элементы А могут иметь несколько ключей сортировки. Даже если А — это массив целых чисел, термин «сортировка» должен быть определен: отсортировать по четным и нечетным? по убыванию? Даже если порядок подразумевается возрастающим, требование остается неоднозначным.
Например, если А = (4, 6, 3, 4, 6, 8), все три приведенные ниже массива удовлетворяют нашему требованию:.
(3, 4, 6, 8), (3, 4, 4, 6, 8) и (3, 4, 4, 6, 6, 8).
Работа с требованием, выраженным в естественной форме, может упростить задачу. Например:.
Возвращает отсортированный элемент всех отдельных элементов массива А.
К сожалению, слово отдельный также неоднозначно. Так можно до бесконечности перефразировать требование, но это будет лишь борьбой с математикой. Мы вернемся далее в главе к этому конкретному примеру.
Общая нотация для формального выражения требований называется Z-спецификацией. Z-спецификации являются стандартным способом описания требуемого состояния до и после процедуры. Ниже приведена некоторая выборка из Z-нотации.
4.8.1.1. Математическая нотация.
=> означает импликацию или логическое следование. Таким образом, Р=> Q означает: «если утверждение Р истинно, то утверждение Q также истинно».
V означает для любого. Например, V живого человека L, L имеет голову.
л означает «и»; v означает «или».
N означает множество натуральных чисел.
Если А множество, то а е А означает, что элемент а принадлежит множеству А. Например, 17 е N.
Acfi означает, что А является подмножеством В, то есть каждый элемент множества А является элементом множества В. Например, Е с N, где Е — множество четных чисел.
А у. В, где А и В множества, означает прямое произведение множеств, то есть множество пар элементов (а, Ь), где а е A, b е В.
Конечные множества можно обозначать с помощью фигурных скобок и запятых. Например, {4,7,2} означает множество, состоящее из элементов 4, 7 и 2. В этой нотации элементы не повторяются и их порядок не имеет значения.
{х: Р} означает множество элементов л обладающих свойством Р. Например, {т. х* = х + 3} означает множество чисел, квадраты которых равны самому числу плюс 3.
Если S конечное множество, то card(.S) означает количество элементов 5. Например, card({g, е, q}) = 3.
4.8.1.1.1. Функции.
Функция /из множества А в множество В — это подмножество прямого произведения Ах В, причем ни один элемент из А не встречается более одного раза в качестве первого элемента пары. Это обозначается так: /: Л —> 5. В формальной записи: /сЛхВл [[(а, й,) е / л (a, b
) е f] => [6, = 6,]].
Областью определения функции называется множество элементов, которые встречаются в качестве первых элементов множества пар, образующих функцию. Например, областью определения функции g: N -> N, где g = {(3, 5), (7, 1), (8, 5)}, является множество {3, 7, 8}.
Областью значений функции называется множество элементов, которые встречаются в качестве вторых элементов множества пар, образующих функцию. Например, областью определения функции g. N ^ N, где g = {(3, 5), (7, 1), (8, 5)}, является множество {5, 1}.
Если элемент у принадлежит области значений функции /, то /~у) означает множество элементов из области определения, которые отображаются на элемент у. Формально f~y) = {х: f(x) = у}. Например, для функции g, определенной выше, g"(5) = {3, 8}.
/ : R Р означает, что / является частичной функцией из R в Р, то есть, где а с R Например, R = {4, 7, 2}, Р = {8, 1, 5}, Q = {4, 7}, /(4) = 5, /(7) = 8.
а н> b означает, что а отображается в b в контексте определяемой функции. Например, для определенной выше функции / 4ь-»5и7|->8.
Пусть / : А В и g : A В. Тогда / © g означает расширение (или доопределение) функции g функцией /. Если х принадлежит области определения g, то ф © g(x) = g(x), а если х не принадлежит области определения g, но принадлежит области определения /, то / © g = f(x). Так, используя функцию / из предыдущего примера и определяя g(4) = И и g(8) = 3, имеем: / Ф g(4) =11, / © = 3 и / Ф g(7) = 8.
4.8.2. Примеры формальных спецификаций.
Z-спецификация состоит из двух соединенных прямоугольников с отсутствующими углами. Верхний прямоугольник описывает типы входных данных, параметров и выходных данных. Нижний прямоугольник описывает состояние (ситуацию) после применения процедуры. Штрих после имени переменной указывает на значение переменной после выполнения процедуры. Например, х — это значение переменной х после выполнения определенной процедуры. Входные данные обозначаются вопросительными знаками. (Вместо слов «на вход подается г» мы просто пишем z?.) Как только вы ближе познакомитесь с этой нотацией, вам станет удобнее ее использовать.
Этот раздел показывает, как использовать Z-спецификации, во-первых, для определения требований к приращению таблицы и, во-вторых, для выполнения поиска в таблице.
Пример 1: Приращение таблицы.
Предположим, что мы хотим точно определить процедуру, которая получает на вход два целочисленных значения / и г и обновляет двумерную таблицу t, содержащую целые числа, путем либо добавления пары (/, г), если / в таблице отсутствует как первый элемент, либо изменения значения г, если / присутствует в таблице как первый элемент. Предполагается, что в таблице t нет повторяющихся первых элементов: на самом деле такие таблицы являются функциями из целых в целые числа.
Мы построим Z-спецификацию приращения таблицы целых чисел без повторяющихся первых элементов. В ней сказано, что t обновляется процедурой Приращение в новую функцию t. Функция V ведет себя аналогично t за исключением отображения / в г.
Например, пусть t — это следующая таблица:
5
7
2
11
1
2
Вызов функции Приращение с входной парой (2, 4) вернет таблицу:
5
7
2
4
1
2
а вызов функции Приращение с входной парой (3, 6) вернет таблицу:
5
7
2
4
1
2
3
6
Z-спецификация изображена на рис. 4.17 и 4.18. Для читателя, достаточно хорошо знакомого с Z-нотацией, эта спецификация предельно однозначна.
Пример 2: Поиск.
На рис. 4.19 определена функция Поиск, которая просматривает все записи в таблице и возвращает результат (соответствующее целое число), если запись найдена, либо нуль в противном случае. Согласно Z-спецификации, t — это таблица, I? — входные данные (которые мы ищем) и г! — целочисленный результат, если I? найдена в левом столбце таблицы. Например, применение функции Поиск к таблицам, приведенным выше, со значением /?, равным 5, даст результат 7 для г/. Спецификация вернет нулевое значение г/, если в левом столбце таблицы I? не найдено.
Вот как можно прочитать результат (внизу).
1.
Либо входной параметр I? не принадлежит области определения t, тогда вернуть нуль и оставить t без изменений.
2.
Либо входной параметр I? принадлежит области определения t, тогда г! — это результат применения t к /?, t остается без изменений.
Почему нам приходится отдельно определять, что t остается без изменений? Потому что если мы не определим этого, мы не сможем пожаловаться на реализацию, меняющую t (возможно, для удобства программирования), поскольку такая реализация тоже будет удовлетворять требованиям.
Рис. 4.17. Частичная Z-спецификация для Приращения таблицы: объяснение символов
Рис. 4.18. Полная Z-спецификация для Приращения таблицы
Рис. 4.19. Z-спецификация для Поиска записи в таблице
Пример 3: Сортировка.
В этом примере мы вернемся к примеру сортировки, приведенному в начале этого раздела. Вспомните, что требование на (несовершенном) естественном языке было таким:.
Вернуть отсортированный массив всех отдельных элементов массива.
В математике массивы — это функции с областью определения {1, 2, 3.....п}, где.
п —- некоторое положительное целое число. Например, массив А = (4, 6, 3, 4, 6, 8) эквивалентен функции, показанной в табл. 4.5.
Таблица 4.5. Массив А
Элемент области определения А
Элемент области значений А(х)
1
4
2
6
3
3
4
4
5
6
6
8
Мы хотим, чтобы в Z-спецификации было сказано, что t имеет ту же область определения и те же элементы, что и t, что ее элементы упорядочены (и могут быть равны). Легко выразить факт, что элементы А упорядочены, но нам также придется и учесть повторение элементов. Вспомните, что в математических множествах элементы не повторяются. Например, 6 встречается дважды среди элементов А. Мы должны убедиться, что 6 также встречается дважды среди элементов А. Для этого мы можем использовать нотацию обратной функции / . Значение /~х) — это множество элементов области определения, отображающихся на х. Множество элементов из А, отображающихся на 6, — это 1 и 5, так что А~
(6) = {1, 5}. Нам только осталось убедиться, что £~(6) также содержит ровно два элемента, чтобы убедиться в корректности повторений элементов в Л и А. Таким образом, нам нужно проверить, одинаковы ли размеры множеств Ах) и Ах) для соответствующих значений х.
Используя Z-нотацию, мы получим спецификацию, показанную на рис. 4.20.
Рис. 4.20. Z-спецификация для примера Сортировка
Пример 4: Максимум.
В качестве последнего примера возьмем распространенное простое требование, которое можно сформулировать формально и неформально. На этот раз мы сформулируем его сначала формально (рис. 4.21). Это спецификация максимума массива целых чисел t и индекса максимального элемента. Если существует несколько максимальных элементов массива, следует выбрать элемент с наименьшим индексом.
Рис. 4.21. Z-спецификация для максимума
4.8.3. Когда следует использовать формальную спецификацию.
Мнения относительно использования формальной спецификации разделились. Несколько коммерческих систем, в том числе широко распространенная система CICS (Customer Information Control System — система управления информацией заказчика) от IBM, использовали формальные методы, в частности Z-спецификации.
Эффективность во взаимодействии требований является показательным тестом, позволяющим решить, формальные или неформальные требования следует использовать. Выражение требований иногда лучше всего осуществить с помощью естественного языка, а иногда — в виде формальной спецификации. Для того чтобы последнее было эффективно, разработчики и пользователи спецификации требований к программному обеспечению должны быть обучены соответствующим образом. Z-спецификации показали себя наиболее применимыми в процессах, которые можно описать в терминах выходных данных (например, сам алгоритм не нужно описывать). Исследования в области формальных спецификаций продолжаются. К ним также относится изучение использования формальных спецификаций для определения графических пользовательских интерфейсов. Достижения математики трудно не оценить. Вполне возможно, что новые исследования расширят практическую применимость формальных методов.
Короче говоря, если требование можно объяснить в терминах конкретных выходных данных и разработчики обучены формальным методам, формальные методы могут стать существенным средством выражения детальных требований. В любом случае мы можем позаимствовать конкретные части формальных спецификаций, такие как использование обозначений ! и ?.
Формальные спецификации необходимы при реализации исполняемых спецификаций (например, [97]). Это спецификации, которые можно автоматически перевести в объектный код. Следовательно, они должны быть четкими и точными. Активные исследования в этой области успешно проводились на протяжении многих лет (например, [87]). Опубликованным примером формальных требований для большого программного приложения реального времени является программа управления полетами для американского флота [All]. Также следует отметить, что эти детальные требования были написаны в системе изготовителя для демонстрации.
Мы вернемся к применению математики в главе 7, когда будем обсуждать реализацию функций. Сайты [ИЗ] также предоставляют современную информацию о Z-спецификациях.
4.8.4. Предусловия и постусловия.
Широко используемый тип спецификаций, менее формальный, чем Z-спецификации, состоит из предусловий и постусловий. Это описание требуемого состояния программы до и после вычислений. Предусловия и постусловия обычно используют псевдокод, однако некоторые их части пишутся на языке реализации (Java, С++ и т. д.). По этой причине они часто используются для определения проектных решений, точнее, они обычно используются для определения требований к функции для программистов. (Вспомните, что, хотя мы называем «требованиями» только одну фазу водопадного процесса, результаты на любой фазе, например на фазе проектирования, становятся требованиями для следующей фазы. В этом случае «реализации требований» — это на самом деле термин, применимый ко многим фазам.).
В качестве примера приведенную ранее задачу нахождения максимума можно сформулировать, как показано на рис. 4.22.
Это требование можно сформулировать более кратко (упражнение 04.4).
Рис. 4.22. Z-спецификации и предусловия с постусловиями для задачи поиска максимума
4.9. Влияние процесса составления D-требований на проект.
После того как D-требования собраны, должны обновляться документы проекта. Как пример рассмотрим необходимые обновления для SPMP.
4.9.1. Влияние на SPMP.
Как только все D-требования собраны, SPMP можно обновлять (табл. 4.6). D-требования передаются под управление конфигурациями. Один из вопросов, которые нужно обсудить, — какой уровень детализации будет считаться элементом конфигурации программы. Конечно, таким элементом может быть раздел 3 («Детальные требования») из SRS (в стандарте IEEE). Каждый класс может быть элементом конфигурации. Отдельные требования обычно слишком малы, чтобы выносить их в отдельный элемент конфигурации.
Таблица 4.6. Обновление проекта после завершения D-требований
Статус после.
предварительного.
варианта
Результат обновления С-требований
Результат обновления D-требований
Вехи
Исходный
Более подробны
Более подробны
Риски
Определить
Исключить риск, определенный раньше. Поиск новых факторов риска
Исключить риск, определенный раньше. Идентифицировать новые факторы риска
Планграфик
Очень высокий уровень
Предварительный план проекта
Более подробный: показывает классы и методы задач разработки
Таблица 4.6 (продолжение)
Статус после.
предварительного.
варианта
Результат обновления С-требований
Результат обновления D-требований
Персонал
Назначить.
разработчиков.
С-требований
Разработчики, назначенные для анализа D-требований
Назначить ответственного за архитектуру программы
Оценка стоимости
Первичные оценки
Первые оценки на основе.
содержимого работы
Улучшенная оценка на базе более конкретной оценки функционального размера или предыдущего опыта с подобными отдельными требованиями
4.9.2. Влияние размера на D-требования.
Когда список требований разрастается до сотен элементов, легко может возникнуть несогласованность, упоминавшаяся в разделе 4.3.6. Классификация требований по классам, классов по пакетам и т. д. становится необходимостью. Группы обычно соответствуют подсистемам в общей организации программного приложения.
Хотя полнота — это цель, к которой мы стремимся при сборе требований, она может стать иллюзорной целью. Для существенных программных приложений редко бывает «последнее» требование — оно последнее лишь до момента фиксации требований.
Как было отмечено в соответствующем разделе предыдущей главы относительно масштабов проекта, крупномасштабные проекты требуют более формальной организации (не путать с формальными методами). SRS приходится разбивать на несколько томов. Отдельный раздел в нашем (крошечном!) примере мог бы разрастись до 700 страниц. Для планирования разработки и проверки D-требований необходимо было бы выполнить огромный объем работы. Проекты с сотнями детальных требований нуждаются в инструментах управления требованиями. Опыт успешного широко распространенного применения пакетов Java показал, что большими наборами требований можно управлять, когда функциональность организована четко определенными пакетами и классами.
Вознаграждение за хороший анализ требований существенно. И наоборот, расплата за скудные требования также значительна. Например, в [107] приводится проведенное Правительственным экономическим отделом исследование одного проекта, в котором «проблемы, связанные с требованиями» привели к превышению бюджета на 600 миллионов долларов, задержке в восемь лет и урезанным возможностям. До сих пор ведутся яростные дебаты о проценте крупных неудачных проектов по отношению к проценту успешно завершившихся. Достаточно сказать, что многие крупные проекты уделяют значительное внимание анализу требований. Автор может подтвердить это благодаря личному опыту.
4.10. Подведение итогов процесса определения D-требований.
D-требования (требования разработчиков или детальные требования) пишутся преимущественно для проектировщиков и разработчиков. Они создаются из С-требований, а также в результате длительных переговоров с заказчиком. D-требования должны быть тестируемыми, прослеживаемыми и согласованными. Поскольку они разрастаются до больших объемов, их следует систематически классифицировать. Удобным способом организации D-требований является классификация по основным классам предметной области и функциям. Существует несколько метрик, по которым можно проверить D-требования. Хороший анализ требований приносит значительную пользу. Подведем итоги этой главы:.
♦ D-требования предназначены для разработчиков.
♦ Цели составления D-требований: ясность, прослеживаемость требований.
♦ Хорошая организация приносит большую пользу. Пример — объектно-ориентрированный стиль.
♦ Следует по возможности использовать формальные методы.
♦ Результатом является обновление SPMP.
Руководство по учебному проекту. D-требования для примера игры Встреча.
В этом разделе объясняется, как принципы получения и представления D-требований, описанные в данной главе, можно применить на практике. Студенту также предлагается обратить внимание на врезки «Один из способов...» в этой главе, поскольку они служат руководством к процессу.
Этап 1. Подготовка.
Халл и Карен закончили написание С-требований на основе обсуждений и интервью с Бетти Симз и Арланом Ховардом. Они использовали заголовки стандарта IEEE (раздел 3.1.4) в качестве подсказок для нефункциональных требований, таких как требования, относящиеся к графическому пользовательскому интерфейсу, производительности и аппаратным платформам. Теперь им нужно определить способ организации функциональных D-требований. Они предвидели необходимость многочисленных повторных обращений и исправлений в SRS и хотели упростить этот процесс насколько это возможно. В результате их основным критерием стала возможность легко поддерживать согласованность между SRS, проектированием и кодом.
Сначала они обсудили организацию детальных требований по состояниям и действиям, базируясь на диаграмме переходов состояний, которая была описана в С-требованиях. Этот способ организации состоял бы из списка действий, предпринимаемых игроком, таких как щелчок на гиперссылке в зоне, после которого должны следовать результаты действия. Они оба согласились, что эта организация была бы понятна, но решили, что она не будет прослеживаться в реализации настолько хорошо, как им бы этого хотелось. Они начали поиск других способов организации D-требований.
Халл выступил за организацию функциональных D-требований по вариантам использования, поскольку он хотел следовать USDP. Он указал, что на этом этапе видеоигру легче всего представлять себе как варианты использования «Инициализировать», «Перейти в соседнюю зону» и «Вступить в контакт с внешним персонажем». Он также отметил, насколько удобно было бы обойтись этими тремя вариантами использования для описания всех функциональных требований. Ему также нравилась мысль о возможности повторного использования этих вариантов для определения будущих игр.
Карен согласилась, что требования будет легче понять, если организовать их по вариантам использования, однако у нее было несколько возражений. Во-первых, некоторые требования будут участвовать более чем в одном варианте использования. В качестве примера она привела ситуацию, когда пользователь щелкает на гиперссылке выхода из комнаты. Это может быть частью всех трех вариантов использования, определенных ими, и будет неясно, где искать конкретное требование. Во-вторых, отображение вариантов использования на код может быть сложнее, чем при организации, задуманной ею. Наконец, Карен отметила, что компания еще не имеет соответствующего архива вариантов использования для будущих работ.
Карен хотела организовать функциональные требования по классам, что, по ее мнению, облегчало просЛеживаемость между требованиями и кодом. Она хотела достаточно аккуратно вобрать классы, чтобы убедиться, что они будут использованы при проектировании (и реализации). Халл отметил недостаток такого подхода: тот факт, что им придется довольно рано утвердить несколько классов, которые будут использованы при реализации приложения. Он беспокоился о том, что, возможно, в будущем у них с Карен может измениться мнение относительно набора выбранных классов. После дальнейшего обсуждения они решили, что организация требований по классам выгоднее, и остановились на этом способе. Однако они решили быть очень осторожными в выборе классов.
Этап 2. Классификация D-требований.
Сначала Халл и Карен рассмотрели каждый вариант использования и изобразили его на диаграмме последовательности. Изучая каждый шаг, они определили, какой объект какого класса инициирует действие и какой объект отвечает за выполнение действия. Этот процесс помог им создавать и (или) определять классы. Им пришлось несколько раз звонить Бетти и Арлану для уточнения шагов вариантов использования, которые они не совсем четко понимали.
Халл перечислил классы и объекты, упоминавшиеся в вариантах использования. Затем Халл и Карен постарались обсудить каждый аспект игры, какой только могли придумать, на предмет дополнительных возможных классов. На последнем шаге в процессе выбора классов они сильно сократили список, оставив лишь несколько самых важных классов и в то же время стараясь сохранить соответствие всех классов вариантам использования. Получившийся список содержал классы: Зона, ПерсонажВстречи, ИграВстреча, Контакт, ОкноКонтакта, ГиперссылкаСоединения, ВнешнийПерсонаж, ПерсонажИгрока и ОкноХарактеристикИгрока.
Далее они окончательно сформулировали заголовки SRS в разделе 3.2 («Детальные требования»). Детальные требования, соответствующие классу Зона, они сгруппировали по зонам игры в подразделе З.2.А. Эти подразделы они упорядочили по алфавиту, поскольку предвидели добавление классов в будущем. Они предположили, что если упорядочить разделы требований по номерам (например, классу ПерсонажИгрока будет соответствовать подраздел номер 3.2.14), то найти конкретное требование будет сложнее, поскольку пользователю SRS придется пролистать многочисленные подразделы 3.2.N, прежде чем он найдет необходимое ему требование. Следующему классу, СоединениеЗоныВстречи, они отвели подраздел 3.2.СЗВ, и т. д. Внутри каждого такого подраздела они создали подразделы для атрибутов, объектов, функциональности и событий.
Этап 3. Написание D-требований.
Карен и Халл написали раздел 3.1 о пользовательских интерфейсах, предоставив детали на рисунках, которые они сделали еще для С-требований, и затем попросили Бетти и отдел инженерной психологии просмотреть его. Зная, что это будет финальный документ, по которому будет создаваться программное приложение, они утвердили с заказчиком каждую деталь.
Карен и Халл привели диаграммы последовательности (см. рис. 4.37, рис. 4.35 и рис. 4.28).
Они проверили свои заметки из интервью с Бетти и Арланом на предмет свойств (атрибутов) каждого раздела классификации (класса). Например, они спросили, какие свойства должны быть у соединений между двумя зонами. (Одним свойством такого соединения должна быть первая зона, другим вторая зона.) Для каждого класса они спросили самих себя, какие объекты этого класса будут необходимы в игре. Например, должны будут существовать объекты гардероб и двор класса Зона. Затем они поинтересовались, какой функциональностью должен обладать класс. Например, функциональность каждого персонажа игры Встреча — это возможность изменять значения характеристик (требование 3.2.ПВ.3.2). Наконец, они перечислили все события, на которые должны будут реагировать объекты класса. (Например, щелчок на гиперссылке выхода из зоны.).
Один беспокоивший их аспект заключался во времени, необходимом для того, чтобы новые значения характеристик начинали действовать. Они поняли, что это ключевой момент в игре: если это будет происходить мгновенно, игрок просто будет устанавливать характеристики, соответствующие данной зоне, на максимум, и игра потеряет интерес. Задержка придаст интерес игре, но проблема в том, какова должна быть эта задержка? Халл и Карен сначала хотели отложить решение этой проблемы, но в конце концов решили сделать задержку продолжительностью в четыре секунды, считая, что изменить это число будет достаточно просто.
Карен беспокоилась о влиянии некоторых требований, особенно тех, от которых зависит обмен характеристиками при контакте. Она боялась, что программисты могут не понять эти требования. Это привело бы к потере времени на исправление ошибок либо к некорректно работающей игре. Она предложила использовать Z-спецификацию. Халл заметил, что никто, кроме Карен, не сможет понять эту спецификацию, поскольку остальная часть команды не была этому обучена. В результате они договорились использовать в определении требования подходящий математический аппарат, но не формат Z-спецификации. Карен отметила пор себя, что если бы она преподавала разработку программного обеспечения, она бы настаивала, чтобы студенты свободно пользовались Z-спецификацией.
Используя заголовки разделов из IEEE SRS, Карен и Халл проверили, все ли исполнительные требования они перечислили (в основном требования, относящиеся к скорости игры, что может повлиять на интерес), и предоставили их на рассмотрение Бетти и Арлану. Они также прошлись по ограничениям памяти (оперативная память и диск), после чего завершили работу над документом.
Этап 4. Завершение: метрики и итоги.
Группа, анализировавшая требования, попросила Бетти, Арлана и остальных членов команды проверить D-требования. Проверка в основном свелась к сравнению с С-требованиями с целью убедиться, что все моменты С-требований включены в D-требования. Также был составлен список, использованный в табл. 4.3. Было обнаружено несколько недостатков, которые Халл с Карен записали и исправили. Результаты этого процесса оказались похожи на результаты, описанные в руководстве по учебному проекту для С-требований (глава 3).
Упражнения.
Ответы и подсказки для упражнений, помеченных символами «о» или «п», приводятся в конце этой главы.
Вопросы для проверки.
П4.1". Для кого преимущественно создаются D-требования? П4.2
. У типичной программы есть много требований. Назовите важную проблему, сопутствующую созданию требований и работе с ними. П4.3". Назовите 3-5 категорий детальных требований. П4.4". Назовите 5-7 желаемых свойств детальных требований. П4.5
. Назовите 4-6 способов организации детальных требований. П4.6".
1.
Существует ли диаграмма последовательности, соответствующая каждому варианту использования?.
2.
Существует ли вариант использования, соответствующий каждой диаграмме последовательности?.
Общие упражнения.
04.1". Напишите десять D-требований для программы, эмулирующей обслуживание клиентов в банке.
04.2.
Что неправильно в приведенных ниже D-требованиях?.
1.
Приложение ДомашнийБюджет должно предоставлять удобный интерфейс для ввода личных данных.
2.
Приложение УправлениеСпутниками должно вычислять предполагаемое время, которое займет оборот вокруг Земли по текущей орбите, и время, фактически затраченное для облета Земли по предыдущей орбите.
3.
Прогорамма КоролъИнвестиций должна определять наилучшую стратегию инвестиций.
04.3.
Укажите точные метрики качества D-требований в упражнении 04.2.
04.4.
Сформулируйте требования к вычислению максимума в примере 4 из раздела 4.8.2 в более сжатой форме. Используйте форму предусловий и постусловий.
04.5.
Приведите вариант использования и соответствующую диаграмму переходов состояний для системы со следующим С-требованием:.
Система должна давать советы для новичка — пользователя Windows — относительно того, как выполнять операции Windows.
Упражнения в команде.
К4.1. (SRS.).
Напишите SRS для вашей программы. Используйте или модифицируйте стандарт IEEE. Если вы используете итеративный процесс разработки, постарайтесь указать, какие требования должны быть реализованы на какой итерации.
Ведите учет времени, затраченного на это, у каждого отдельного человека и у группы. Разбейте все время на соответствующие действия. Измерьте эффективность вашей работы. (Можете разработать свои собственные метрики; обратите также внимание на командные упражнения в предыдущих главах.) Укажите, как ваш процесс разработки спецификации можно было бы улучшить.
Критерии оценки.
1.
Уровень четкости («Отлично» — очень четко написано).
2.
Степень, в которой план включает все важные детали и не включает неважный материал («Отлично» — содержит более 95 % важных деталей и менее 5 % несущественных деталей).
3.
Эффективность вашей самооценки и описание улучшения процесса («Отлично» — очень эффективные метрики; очень конкретные предложения относительно того, как процесс можно было бы улучшить с небольшими временными издержками).
Подсказки.
04.1. Упорядочить по классам. Должны быть добавлены следующие классы: КлиентБанка, Банк, Кассир.
Ответы.
П4.1. Преимущественно разработчики; клиенты — вторично. П4.2. Классифицировать их так, чтобы их можно было легко находить и поддерживать.
П4.3. Функциональные, нефункциональные, обратные, интерфейсные, проектные и ограничения реализации.
П4.4. Прослеживаемость, тестируемость, однозначность, приоритет, полнота, условия ошибок, согласованность.
П4.5. По режиму, действующему лицу варианта использования, классу (объектно-ориентированный стиль), характеристикам, иерархии функций или состояниям. П4.6.
1.
Да. Всегда можно определить затронутые в варианте использования объекты, а затем преобразовать последовательность действий пользователя (системы) в последовательность вызовов функций этих объектов.
2.
Нет. Диаграмма последовательности выражает последовательность вызовов функций. Не каждая такая последовательность представляет типичное взаимодействие между пользователем и программой.
Пример. Спецификация требований к программному обеспечению (SRS) для видеоигры Встреча, часть 2.
История версий этого документа.
♦ x/yy/zzz Исходный черновик, написан Карен Петере.
4- x/yy/zzz Техническая точность, проверена Халом Фурнессом; требования.
классов Зона и ПерсонажИгрока изменены. 4- x/yy/zzz Версия 0.1 принята Карен Петере.
3. Детальные требования.
3.1. Требования к внешнему интерфейсу 3.1.1. Пользовательские интерфейсы.
[Примечание для студентов. Раздел 2.1.2 в SRS видеоигры Встреча содержит лишь наброски пользовательских интерфейсов для общего представления продукта. В них не хватает деталей, и они не должны рассматриваться как окончательные.
Если пользовательские интерфейсы не будут полностью определены далее в этом документе, все детали для этого должны быть даны в этом разделе. Поскольку в этом примере мы используем объектный стиль спецификации, подробности каждого окна находятся в соответствующих классах в разделе 2.2 SRS.
В любом случае этот раздел должен объяснить физическую связь между графическими элементами (например, расположение каскадом, и т. д.).].
Действие игры Встреча происходит в зонах. Типичный экранный снимок зоны двор с персонажем, контролируемым игроком, внешним персонажем и результатами контакта показан на рис. 4.23. Этот интерфейс занимает весь экран монитора. Зоны соединены с соседними зонами. Эти соединения обозначены гиперссылками. Щелчок на одной из гиперссылок переносит персонаж игрока в соответствующую зону.
Рис. 4.23. Двор: изображены игровые персонажи и окно статуса
Вот полный набор интерфейсов.
1.
Один пользовательский интерфейс для каждой зоны, определенной в разделе 3.2.3 ниже.
2.
Пользовательский интерфейс, позволяющий настроить значения характеристик персонажа игрока, определен в разделе 3.2.ХИ.
3.
Пользовательский интерфейс для показа результатов контакта, определенный в разделе 3.2.КД. Тот же пользовательский интерфейс используется для показа статуса персонажа игрока.
Интерфейс типа 1 всегда будет присутствовать на мониторе. В случаях, определенных этими требованиями, интерфейсы типов 2 и 3 будут показаны поверх первого. Это требование зафиксировано в STD (здесь должна быть ссылка на тест).
3.1.2.
Аппаратные интерфейсы.
[Примечание для студентов. Оборудование, на котором будет работать игра Встреча.] Нет.
[Будущие версии: для игры можно будет использовать джойстик.].
3.1.3.
Программные интерфейсы.
[Примечание для студентов. Другие программы, с которыми игра должна взаимодействовать, например драйвер принтера.] Нет.
[Будущие версии: в игру можно будет играть с сайта Intergalactic Internet Gaming Site.].
3.1.4.
Коммуникационные интерфейсы.
Нет.
[Будущие версии: игра Встреча будет иметь интерфейс для выхода в Интернет посредством модема с минимальной скоростью 56 Кбайт/с.].
3.2. Детальные требования.
[Примечание для студентов. В этом разделе вы располагаете некоторой свободой относительно стандартов IEEE, чтобы учесть варианты использования. Сначала здесь представлены диаграммы последовательности, необходимые для описания вариантов использования из раздела 2.2 данной SRS (раздел 3.2.1). Классы, необходимые для описания этих вариантов использования, затем применяются для классификации детальных требований (раздел 3.2.2 SRS). Диаграммы последовательности объяснены в разделе 4.4 этой главы.].
3.2.1. Диаграммы последовательности.
[Примечание для студентов. Здесь мы покажем диаграммы последовательности для каждого варианта использования, определенного в разделе 2.1 данной SRS. В требованиях стандарта IEEE нет раздела «Диаграммы последовательности»; он добавлен для удобства.].
3.2.1.1. Вариант использования «Инициализировать».
Диаграмма последовательности для варианта использования «Инициализировать» показана на рис. 4.24. Для этого варианта использования необходимы такие классы, как ИграВстреча (у которого будет только один объект), ПерсонажИгрока (объектом которого будет главный персонаж игрока), ОкноХарактеристикИгрока (у которого будет только один объект) и Зона (объектом которого будет гардероб).
Рис. 4.24. Диаграмма последовательности для варианта использования «Инициализировать»
3.2.1.2. Вариант использования «Перейти в соседнюю зону».
Диаграмма последовательности для варианта использования «Перейти в соседнюю зону» изображена на рис. 4.25. Для этого варианта использования необходимы классы ГиперссылкаСоединения, СоединеииеЗоны и ПерсонажИгрока.
Рис. 4.25. Диаграмма последовательности для варианта использования «Перейти в соседнюю зону»
3.2.1.3. Вариант использования «Вступить в контакт с внешним персонажем».
Диаграмма последовательности для варианта использования «Вступить в контакт с внешним персонажем» показана на рис. 4.26. Для этого варианта использования необходимы классы ИграВстреча (у которого будет только один объект), ВнешнийПерсонаж (например, объект Фредди), Контакт, ПерсонажИгрока, ОкноХарактеристикИгрока и ОкноКонтакта.
Рис. 4.26. Диаграмма последовательности для варианта использования «Вступить в контакт с внешним персонажем»
3.2.2. Классы для классификации детальных требований.
[Примечание для студентов. Поскольку мы упорядочиваем детальные требования по классам, прежде всего мы должны перечислить имеющиеся выбранные классы. Сюда относятся не все классы, которые будут использоваться в программе, а так называемые основные классы, принадлежащие классам предметной области программы. Эти классы удобны для организации всех требований. В данном случае, например, все они являются аспектами видеоигры Встреча.].
Для выражения требований достаточно следующих классов видеоигры Встреча: Зона, ПерсонажВстречи, ИграВстреча, ОкноКонтакта, ВнешнийПерсонаж, ПерсонажИгрока, и ОкноХарактеристикИгрока. Они показаны на объектной модели (рис. 4.27).
[Примечание для студентов. Нумерация «3.2.3ona.N.N...» и т. д., используемая в разделе 3.2, упрощает операции добавления, удаления и поиска требований путем упорядочения классов, содержащих требования, по алфавиту. Представьте себе, что у вас есть сотни требований. Если мы будем нумеровать классы «3.2.1...»,
«3.2.2...» и т. д., то добавлять новые классы придется в конец списка, поскольку на имеющуюся нумерацию уже будут ссылки в разных местах проекта и ее нельзя будет менять. Требования в этом случае не будут упорядочены по алфавиту. В результате при поиске конкретного требования вам придется читать весь список подряд.]
Рис. 4.27. Классы для видеоигры Встреча с отмеченным отношением наследования
3.2.ВП. Внешние персонажи.
Внешний персонаж — это персонаж игры Встреча, не управляемый игроком. 3.2.ВП.1. Атрибуты внешних персонажей.
См. требования к персонажу Встречи. Требования для этого класса должны быть теми же.
[В будущих версиях внешние персонажи могут видоизменяться.] 3.2.ВП.2. Объекты — внешние персонажи.
[Примечание для студентов. В этом разделе указано, что будет существовать только один внешний персонаж.].
3.2.ВП.2.1. Фредди — внешний персонаж [важно; еще не реализовано].
[Примечание для студентов. Утверждение в скобках выше указывает приоритет и статус требования. Как только требование будет закодировано и протестировано, фразу «еще не реализовано» можно будет убрать или заменить на «реализовано».
Требования, отмеченные словом «важно», должны быть реализованы прежде всех остальных. Как только проект требования для реализации разработан, слово «важно» можно убрать. Это одна из техник прослеживания состояния программы и ее связи с SRS. Другая техника заключается в определении итерации, к которой относится требование.].
Должен существовать внешний персонаж по имени Фредди (рис. 4.28). Этот персонаж изначально будет иметь 100 очков-жизней, распределенных равномерно по всем его характеристикам.
Рис. 4.28. Изображение внешнего персонажа Фредди 3.2.ВГ1.3. Функциональность внешних персонажей.
3.2.ВП.3.1. Движение внешних персонажей [важно; еще не реализовано].
Пока внешний персонаж жив, он должен перемещаться из одной зоны в другую, соседнюю, через случайные промежутки времени со средней длительностью две секунды. После присутствия в зоне в течение случайного промежутка времени со средним значением в одну секунду все очки-жизни персонажа распределяются между характеристиками, специфичными для зоны, так чтобы значения каждой из таких характеристик были по возможности равными.
3.2.ГС. Гиперссылки соединения между зонами.
Гиперссылки соединения — это гиперссылки, расположенные на выходе из каждой зоны и показывающие зону, в которую они ведут.
3.2.ГС.1. Атрибуты гиперссылок соединения.
3.2.ГС.1.1. Соединение [важно; еще не реализовано].
Каждой гиперссылке соединения соответствует зона, на которую она ссылается.
3.2.ГС.2. Объекты гиперссылки соединения [важно; еще не реализовано].
Существует две гиперссылки соединения, соответствующие соединению между двумя зонами, по одной в каждой соединяемой зоне.
3.2.TC.3. Функциональность гиперссылок соединения.
Нет.
3.2.ГС.4. События, относящиеся к гиперссылкам соединения.
3.2.ГС.4.1. Пользователь щелкает на гиперссылке соединения.
В результате щелчка на гиперссылке соединения персонаж игрока должен быть показан в зоне, на которую указывает гиперссылка.
3.2.30. Зоны.
[Примечание для студентов. Сначала мы опишем, на что ссылается класс (например, данная классификация требований).].
Зона — это видимая область, показываемая па мониторе. Все действия игры (в том числе и контакты) происходят в зонах. Примерами зон могут быть комнаты, сады, дворы.
3.2.30.1.
Атрибуты зоны.
[Примечание для студентов. Здесь мы расскажем, какими свойствами должен обладать каждый объект этого класса.].
3.2.30.1.1.
Название зоны [важно; еще не реализовано].
У каждой зоны будет уникальное название, включающее от 1 до 15 символов. Допустимыми символами будут считаться только пробелы, цифры 0-9, буквы от а до 2 и от А до Z.
План тестирования (здесь должна быть ссылка на тест).
[Примечание для студентов. Каждое требование-атрибут отображается па пару функций get- и set-. Этот документ предлагает способ, которым каждое требование может ссылаться на модульный тест в SDD посредством гиперссылки.].
3.2.30.1.2.
Рисунок зоны [важно; еще не реализовано].
Для изображения каждой зоны на мониторе будет существовать отдельный рисунок. Этот рисунок должен быть размером во весь экран.
3.2.30.1.3.
Зона — особые характеристики [важно; еще не реализовано].
Для особых характеристик зоны должны применяться лишь некоторые характеристики персонажа. Особые характеристики, требуемые для каждой зоны, определены в разделе 3.2.30.2.
3.2.30.1.4.
Кнопки [важно; еще не реализовано].
В каждой зоне должны показываться кнопки Показать статус, Настроить характеристики и Закончить игру в левом нижнем углу.
3.2.30.2.
Объекты — зоны.
[Примечание для студентов. Мы определим конкретные объекты — зоны, которые должны существовать в программе.].
3.2.30.2.1.
Зона «двор» [важно; еще не реализовано].
Должна существовать зона «двор», особыми характеристиками которой будут выносливость и сила. Предварительное изображение двора с прилагающейся картой соседних зон показано на рис. 4.29.
3.2.30.2.2.
Зона «гардероб» [важно; еще не реализовано].
Должна существовать зона «гардероб», не имеющая никаких особых характеристик. Ее предварительное изображение (рис. 4.30) содержит карту соседних зон.
3.2.30.2.3.
Зона «подвал» [важно; еще не реализовано].
Должна существовать зона «подвал», особыми характеристиками которой будут выносливость и терпение. Ее предварительное изображение (рис. 4.31) содержит карту соседних зон.
3.2.30.2.4.
Зона «кухня» [важно; еще не реализовано].
Должна существовать зона «кухня», особой характеристикой которой будет сосредоточенность. Ее предварительное изображение (рис. 4.32) содержит карту соседних зон.
Рис. 4.30. Изображение гардероба в игре Встреча
3.2.30.2.5. Зона «гостиная» [важно; еще не реализовано].
Должна существовать зона «гостиная», особыми характеристиками которой будут выносливость и сосредоточенность. Ее предварительное изображение (рис. 4.33) содержит карту соседних зон.
Рис. 4.32. Изображение кухни в игре Встреча
3.2.30.2.3. Зона «кабинет» [важно; еще не реализовано].
Должна существовать зона «кабинет», особой характеристикой которой будет сосредоточенность. Ее предварительное изображение (рис. 4.34), содержит карту соседних зон.
Рис. 4.34. Изображение кабинета в игре Встреча
3.2.30.3.
Функциональность зон.
[Примечание для студентов. Это обязательная функциональность, свойственная только зонам. Каждая функциональная возможность программы должна относиться к одному из этих разделов.] Нет.
3.2.30.4.
События, относящиеся к зонам.
[Примечание для студентов. Мы отделяем события, относящиеся к зонам, от атрибутов, объектов и методов. Событие — это действие извне, на которое программа реагирует.].
3.2.30.4.1.
Изображение при входе персонажа [важно; еще не реализовано].
Каждый раз, когда главный персонаж игрока вступает в зону, на экране необходимо показать соответствующую зону и персонажи, находящиеся в ней.
3.2.30.4.2.
Обработка контактов [важно; еще не реализовано].
Когда внешний игровой персонаж входит в зону, в которой находится главный персонаж игрока, или наоборот, они вступают в контакт друг с другом.
3.2.30.4.3.
Прерывание контактов [не обязательно; еще не реализовано].
Игроки имеют возможность прерывать контакт случайным образом. В среднем игрок может остановить один из десяти контактов путем запуска процедуры настройки свойств. Пользователь пытается прервать контакт, устанавливая характеристики своего персонажа. Если игра не позволяет это сделать, ничего не произойдет: игра продолжается так, как будто попытки изменить характеристики не было вообще.
3.2.30.4.4.
Щелчок на кнопке Настроить характеристики [не обязательно; еще не реализовано].
Когда пользователь щелкает на кнопке Настроить характеристики, поверх зоны появляется окно для настройки значений характеристик. Это происходит лишь в том случае, если в зоне нет внешних персонажей. Подробное описание этого окна—в разделе 3.2.ХИ.
3.2.30.4.5.
Щелчок на кнопке Закончить игру [не обязательно; еще не реализовано].
Когда пользователь щелкает на кнопке Закончить игру, игра завершает свою работу. Не выводится никаких дополнительных экранов.
[Примечание для студентов. Предыдущее предложение, являющееся обратным требованием, необходимо, поскольку часто игры перед выходом показывают итоги сессии.].
3.2.30.4.6. Щелчок на кнопке Получить статус [не обязательно; еще не реализовано].
Когда пользователь щелкает на кнопке Получить статус, должно появиться окно контакта, в котором должен быть показан статус персонажа игрока до и после последнего контакта.
3.2.ИВ. Игра Встреча.
Требования в этом разделе относятся к игре в целом. 3.2.ИВ.1. Атрибуты игры.
3.2.ИВ.1.1. Продолжительность [не обязательно; еще не реализовано].
Должна храниться запись с информацией о продолжительности каждой игры, с отсчетом времени с момента, когда игрок запускает игру.
3.2.ИВ.2. Объекты класса Игра Встреча.
3.2.ИВ.2.1. Одиночная игра [важно; еще не реализовано].
Игра должна существовать в одном экземпляре.
[Примечание для студентов. В будущих версиях будет возможен одновременный запуск нескольких экземпляров игры.].
3.2.КО. Контакты.
Контакт — это взаимодействие между игровым персонажем, управляемым игроком, и внешним персонажем.
3.2.К0.1. Атрибуты Контактов.
Нет.
3.2.КО.2. Объекты Контакты.
Не будет существовать постоянных объектов этого класса. 3.2.К0.3. Функциональность контактов.
3.2.КО.3.1. Вступление в контакт с внешним персонажем [важно; еще не реализовано].
[Примечание для студентов. Это требование по природе своей является математическим, так что здесь не предпринимается попыток заменить математику естественным языком, что могло бы привести к неточности формулировки. Однако использование естественного языка для объяснения математики приветствуется.] Когда происходит контакт, сильнейшим из двух персонажей считается тот, у которого сумма значений характеристик, определяемых зоной контакта, больше. Система передает половину значений каждой характеристики, определенной зоной, от слабейшего сильнейшему. Передачи значений не происходит, если значения персонажей равны по этим характеристикам.
Если у одного из персонажей не остается очков-жизней после перераспределения значений характеристик, игра завершается. Если игра не завершается, персонаж игрока перемещается в произвольную зону и игроку показываются результаты контакта.
В качестве примера перераспределения значений предположим, что персонаж игрока вступает в контакт с внешним персонажем в зоне, предпочтительными характеристиками которой являются выносливость и сосредоточенность. Пусть p
— значение выносливости игрока. Предположив, что
получим
где штрихом отмечены значения после контакта. (Внимательный читатель найдет недостаток в предыдущем уравнении, где должно быть f =/
/ 2. Оставим этот дефект нетронутым в качестве примера.).
Рассмотрим числовой пример в этой зоне: пусть значение выносливости игрока равно 7, сосредоточенности — 19, а значения выносливости и сосредоточенности Фредди соответственно 11 и 0,6, то есть игрок сильнее. Результаты контакта будут такими:.
Игрок: уверенность 7 + 11/2 = 12,5; сосредоточенность 19 + 0,6 / 2 = 19,3 Фредди: уверенность 11/2 = 5,5; сосредоточенность 0, поскольку 0,6 / 2 меньше 0,5.
3.2.ОК. Окно контакта [важно; еще не реализовано].
Должно существовать окно, показывающее результат контакта. Его формат дан на рис. 4.35.
Рис. 4.35. Пользовательский интерфейс для изображения статуса
3.2.0K.4. События, относящиеся к окну контакта.
3.2.ОК.4.1. Закрытие окна контакта [важно; еще не реализовано].
Когда пользователь щелкает на кнопке 0К, окно должно исчезать.
3.2.ПВ. Персонаж Встречи.
3.2.ПВ.1. Атрибуты Персонажей Встречи.
3.2.ПВ.1.1. Имя [важно; еще не реализовано].
У каждого персонажа в видеоигре Встреча будет уникальное имя. Ограничения на имена персонажей будут те же, что и для названий зон. Они определены в разделе 3.2.30.1.
3.2.ПВ.1.2. Характеристики [важно; еще не реализовано].
У каждого игрового персонажа имеется одинаковый набор характеристик. Каждая характеристика представлена неотрицательным числом с десятичной запятой и как минимум одной цифрой в дробной части. Все они одинаково инициализируются, так что сумма значений всех характеристик равна 100. Значение характеристики не может быть в промежутке 0-0,5.
В первой версии будут такие характеристики, как сосредоточенность, ум, терпение, выносливость и сила.
3.2.ПВ.1.3. Изображение персонажа [важно; еще не реализовано].
Каждый персонаж игры будет представлен картинкой. 3.2.ПВ.2. Объекты персонажей.
Персонажи игры описаны среди типов персонажей игры Встреча. Э.2.ПВ.З. Функциональность персонажей 3.2.ПВ.3.1. Очки-жизни [важно; еще не реализовано].
Игра Встреча будет иметь возможность рассчитать сумму значений характеристик любого персонажа. Эта сумма будет называться числом очков-жизней.
3.2.ПВ.3.2. Возможность настройки характеристик персонажа [важно; еще не реализовано].
Когда игровой персонаж находится в зоне один, пользователь может изменять значения характеристик. Выбранные значения не должны превышать сумму значений всех характеристик. Значения остальных характеристик автоматически настраиваются так, чтобы сохранить соотношения между ними. Исключением является случай, когда значения остальных характеристик меньше единицы — тогда они заменяются на нули.
3.2.ПИ. Персонажи игрока.
Будут существовать персонажи Встречи, управляемые игроком. 3.2.ПИ.1. Атрибуты персонажей игрока.
См. атрибуты персонажа Встречи. Изображение персонажа игрока может быть выбрано из трех (рис. 4.36).
Рис. 4.36. Варианты выбора персонажа игрока
3.2.ПИ.2. Объекты — Персонажи игрока.
3.2.ПИ.2.1. Главный персонаж игрока [важно; еще не реализовано].
Игрок будет управлять некоторым конкретным игровым персонажем, называемым главным персонажем. Природа этого управления подчинена ограничениям, перечисленным в следующих требованиях. Этот персонаж будет изначально иметь 100 очков-жизней, равномерно распределенных между характеристиками персонажа.
3.2.ПИ.2.2. Дополнительные персонажи, управляемые игроком [не обязательно; еще не реализовано].
У игрока будет возможность заводить другие персонажи помимо главного. Подробности еще не определены.
3.2.ПИ.З. Функциональность персонажей игрока.
3.2.ПИ.3.1. Настройка значений характеристик персонажа игрока [важно; еще не реализовано].
Когда в зоне, в которой находится главный персонаж игрока, нет внешних персонажей, игрок может настраивать значения любых характеристик своего главного персонажа, используя ОкноХарактеристикИгрока (рис. 4.38). Выбранное значение не должно превышать суммы значений всех характеристик. Значения остальных характеристик автоматически настраиваются так, чтобы сохранять соотношения между ними, за исключением тех характеристик, значения которых получаются меньше 0,5 — в этом случае им будет присвоено нулевое значение.
3.2.ПИ.3.2. Настройка изображения персонажа игрока [желательно; еще не реализовано].
У игрока будет возможность выбрать внешний вид представляющего его персонажа как минимум из двух вариантов (см. рис. 4.36).
3.2.ПИ.3.3. Вычисление возраста персонажа игрока [не обязательно; еще не реализовано].
У главного персонажа игрока значение каждой характеристики будет автоматически увеличиваться на определенный процент во время первой половины его жизни и уменьшаться на тот же процент во второй половине его жизни. Подробности нужно придумать.
3.2.СВ. Соединения между зонами.
Персонажи переходят из зоны в соседнюю зону посредством соединений. Каждое из них соединяет две зоны. Необходимые соединения зон показаны на рис. 4.37.
3.2.CB.1. Атрибуты соединений.
3.2.СВ.1.1. Первая и вторая зоны [важно; еще не реализовано].
Каждое соединение будет связывать пару зон, которые мы будем называть первой и второй зоной.
Рис. 4.37. Конфигурация зон в игре Встреча (желательное требование) 3.2.СВ.2. Объекты — соединения.
3.2.СВ.2.1. Гардероб — двор [важно; еще не реализовано].
Будет существовать соединение между гардеробом и двором.
3.2.СВ.2.2. Подвал — кабинет [важно; еще не реализовано].
Будет существовать соединение между подвалом и кабинетом.
3.2.СВ.2.3. Кабинет — гостиная [важно; еще не реализовано].
Будет существовать соединение между кабинетом и гостиной.
3.2.СВ.2.4. Двор — гостиная [важно; еще не реализовано].
Будет существовать соединение между двором и гостиной.
3.2.СВ.2.5. Гостиная — подвал [важно; еще не реализовано].
Будет существовать соединение между гостиной и подвалом.
3.2.СВ.2.6. Двор — кухня [важно; еще не реализовано].
Будет существовать соединение между двором и кухней. 3.2.CB.3. Функциональность соединений.
Нет.
3.2.СВ.4. События, соответствующие соединениям.
3.2.СВ.4.1. Перемещение персонажа по соединению [важно; еще не реализовано].
Соединения показываются как гиперссылки на границах зон, когда персонаж находится внутри зоны. Когда пользователь щелкает на такой гиперссылке, показывается соответствующая зона и персонаж в ней.
3.2.ХИ. Окно характеристик игрока.
Это окно, в котором пользователь может изменять значения характеристик своих персонажей.
3.2.ХИ.1. Атрибуты окна характеристик игрока.
Окно для настройки значений характеристик персонажа игрока в игре Встреча показано на рис. 4.38. В центре показывается изображение персонажа игрока, а его имя — в левом верхнем углу экрана. Очки-жизни персонажа показываются посередине. Слева находится список, показывающий одновременно четыре характеристики. Объяснение математических вычислений показано в бледножелтом окне в нижней половине экрана. Цвет фона для имени, очков-жизней и окон со значениями должен быть бледно-бирюзовым.
Рис. 4.38. Пользовательский интерфейс для настройки значений характеристик 3.2.ХИ.2. Объекты — окна характеристик игрока.
3.2.ХИ.2.1. Окно для назначения характеристик [важно; еще не реализовано].
Окно должно быть доступно при условиях, описанных выше, для настройки значений характеристик персонажа игрока. Окно будет выглядеть согласно описанию, приведенному в разделе 3.1.1.2 этой спецификации.
3.2.XH.3. Функциональность характеристик игрока.
3.2.ХИ.3.1. Инициализация [важно; еще не реализовано].
Меню характеристик игрока должно иметь возможность показывать себя.
3.2.ХИ.4. События, относящиеся к окну характеристик игрока.
3.2.ХИ.4.1. Изображение значения характеристики [важно; еще не реализовано].
Когда игрок выбирает характеристику из списка, расположенного слева, значение этой характеристики должно быть показано в текстовом окне справа.
3.2.ХИ.4.2. Изменение значения характеристики [важно; еще не реализовано].
Когда пользователь вводит допустимое значение характеристики и нажимает клавишу Enter, значение характеристики устанавливается равным введенному числу. Если введено неразрешенное значение, должно появиться окно с сообщением об ошибке «Недопустимое значение: попробуйте еще раз».
3.2.ХИ.4.3. Закрытие окна [важно; еще не реализовано].
Когда пользователь щелкает на кнопке ОК, начинается отсчет времени. Через четыре секунды окно пропадает. Изменения настроек характеристик вступают в силу только по истечении четырех секунд.
3.2.ХИ.4.4.
Прерывание [важно; еще не реализовано].
При прерывании окно пропадает.
Заметьте, что прерывания инициируются внешним персонажем, когда он входит в зону. Также заметьте, что в этом случае значения характеристик не изменяются и происходит контакт.
3.3.
Требования к производительности.
[Примечание для студентов. К требованиям производительности относятся необходимая скорость и (или) временные ограничения. Сюда можно также отнести (статическое или динамическое) использование памяти (RAM, жесткий диск), например данные о памяти, необходимой во время работы программы, если это не было документировано в другом разделе SRS.].
Программа должна загружать и показывать исходную картинку менее чем за минуту.
Контакты должны происходить не дольше одной секунды.
Эти требования были протестированы (здесь должна быть ссылка на тест).
3.4.
Ограничения проектирования.
IПримечание для студентов. Этот раздел определяет ограничения, накладываемые на проектирование. Если в этом разделе нет материала, разработчики свободны создавать любой (хороший) проект, который удовлетворит остальным требованиям. Например, мы можем добавить ограничение проектирования «одноэтажный» к следующему требованию: «Дом с четырьмя спальнями, каждая из которых не далее тридцати секунд ходьбы из гостиной».].
Проектирование игры Встреча должно быть выполнено с использованием UML и объектно-ориентированного подхода. Реализация должна быть выполнена на Java. Программа будет запускаться как Java-приложение Windows 95. Она будет разработана таким образом, чтобы было относительно легко изменять правила игры и другие разработчики имели возможность настраивать игру.
3.5.
Атрибуты программной системы 3.5.1. Надежность.
Встреча будет зависать не чаще чем один раз из тысячи. Документация тестирования (здесь должна быть ссылка на тест).
3.5.2.
Доступность.
Встреча должна быть доступна для игры на персональном компьютере под Windows 95 (то есть ни с какой другой программой одновременно). Документация по тестированию (здесь должна быть ссылка).
3.5.3.
Защита.
[В будущих версиях доступ к сохраненным играм будет разрешен только через пароль.].
3.5.4.
Поддержка.
3.5.4.1.
Изменение персонажей и зон [важно].
Должно быть довольно просто изменять персонажи и зоны.
3.5.4.2.
Глобальное изменение стилей [важно].
Должно быть несложно глобально изменить стиль зон и соединений. (Изменения стиля отражают разные уровни игры в одном и том же окружении.).
3.5.4.3.
Изменение правил контакта [не обязательно].
Правила контакта должно быть легко изменить.
3.6. Дополнительные требования.
Нет.
4. Дополнительная информация.
Нет.
4.1.
Оглавление и индекс.
Будет приложено.
4.2.
Приложения.
Будут добавлены.
[Примечание для студентов. Приложения могут содержать следующее.
1.
Пример форматов ввода-вывода, описания стоимости изучения и анализа или результаты опросов пользователей.
2.
Дополнительную информацию, которая может помочь читателям SRS.
3.
Описание проблемы, которую должна решать программа.
4.
Специальные инструкции кодирования и способы защиты, экспорта, начальной загрузки и т. д.
Отдельно уточните, является ли каждое дополнение официальной частью SRS.]
Как точен и чудесен в действии!.
Шекспир. Гамлет.
Разработчики программного обеспечения постоянно ищут ясные и изящные архитектуры для своих проектов, поскольку они облегчают получение реализации без ошибок и дефектов, а также лучше приспособлены к расширению и повторному использованию. Содержание этой главы представлено на рис. 5.1.
♦ Основы: раздел 5.1.
♦ Детали: разделы 5.2-5.6.
♦ Руководство по учебному проекту: архитектура видеоигры Встреча.
♦ Упражнения.
♦ Пример: Проектная документация программного обеспечения (SDD) для видеоигры Встреча, часть 1.
Учебные цели этой главы таковы.
♦ Понимание термина архитектура программного обеспечения.
♦ Использование каркасов, образцов проектирования и моделей.
♦ Разработка вариантов архитектуры.
♦ Отображение архитектуры на детальное проектирование.
♦ Применение стандарта IEEE SDD.
Основы_.
5.1. Введение в архитектуру программ.
В течение десятилетий разработчики программного обеспечения создавали свои проекты либо с нуля, либо используя уже накопленный опыт, если таковой как-то удавалось приобрести. В настоящее время интенсивно развивается дисциплина программных архитектур и проектирования. Теперь мы можем говорить об архитектуре высокого уровня и архитектуре низкого уровня в терминах, понятных всем профессиональным разработчикам. Точно так же, как мы восторгаемся такими инженерными разработками, как туннель между Великобританией и Францией или проект Международной космической станции, вскоре мы будем восхищаться грандиозными программными архитектурами.
Рис. 5.1. Схема процессов разработки программ: темы главы
5.1.1. Обзор технологии разработки.
Любое приложение имеет аппаратные и программные компоненты. Как пример рассмотрим антиблокировочную систему автомобильных тормозов. Она имеет механическую, электронную и программную составляющие и призвана увеличить эффективность торможения, не допуская блокировки колес. Другим примером может служить интерактивный чат в Сети.
Системная разработка — это процесс анализа и проектирования, который разделяет приложение на аппаратные и программные компоненты. Некоторые аспекты этой декомпозиции диктуются требованиями заказчика, другие определяются разработчиками. Рассмотрим процесс декомпозиции на примере возможной конфигурации интернет-версии игры Встреча, запускаемой на сервере GameCorp (рис. 5.2).
Процесс системной разработки начинается с определения общих системных требований. Затем делается выбор оптимального соотношения между аппаратным и программным обеспечением. После этого определяется декомпозиция приложения на аппаратное и программное обеспечение. Затем к программной части применяется технология разработки, начиная с анализа требований и т. д. В цели этой книги не входит попытка охватить всевозможные процессы системной разработки. Мы будем рассматривать проекты с очевидным распределением на аппаратное и программное обеспечение, подобные игре Встреча. В данном примере система физически разделена на компьютеры игроков, игровой сервер и сервер обработки счетов (рис. 5.2). Компоненты программного обеспечения распределены между компьютерами так, как показано на рис. 5.2.
Рис. 5.2. Физическая конфигурация для интернет-ориентированной игры Встреча
Встроенное программное обеспечение взаимодействует на микросекундном уровне с техническими средствами, находящимися в другом месте. Например, программное обеспечение такого рода содержит тормозная система с автоматической антиблокировкой (рис. 5.3). Интернет-ориентированную версию игры Встреча (рис. 5.2), напротив, нельзя рассматривать как встроенное программное обеспечение. Встроенные приложения ставят одну из самых сложных задач перед разработчиком, поскольку для них очень большое значение имеет время реакции. Министерство обороны США достаточно давно использует передовые методы системной разработки, поскольку военные системы требуют очень тесной интеграции программного обеспечения и технических средств. Подрядчики министерства обороны используют огромное количество системных разработчиков. Эти разработчики формируют системные требования и проводят исследования, необходимые для создания подходящей конфигурации. Нормы для системных разработчиков определяются в документах IEEE и других стандартах, таких как IEEE Р1233. Этот стандарт содержит широкий спектр соглашений по таким вопросам, как размер и вес механических компонентов, ограничения ресурсов, ограничения среды, производительность, функциональность, совместимость, надежность, условия сопровождения и технологичность производства.
Рис. 5.3. Диаграмма автоматической антиблокировочной системы (ABS) тормозов
5.1.2.
Что такое архитектура программы.
Если сравнивать разработку программ с процессом постройки моста, то анализ требований будет аналогичен выбору мест, где мост будет начинаться и заканчиваться, а также определению рода нагрузок, которые он должен выдерживать. Далее, архитектор моста должен решить, каким будет мост: подвесным, консольным или какого-то другого типа, удовлетворяющего требованиям. Другими словами, он должен будет определить архитектуру моста. Разработчики программного обеспечения сталкиваются с похожим выбором. В этой главе мы рассматриваем выбор архитектуры для приложения.
Создание архитектуры — это проектирование на самом высоком уровне. Оставшуюся часть процесса проектирования мы будем называть детальным проектированием.
Ясное описание архитектуры очень важно для всех приложений и обязательно в том случае, когда к разработке привлекается большое количество людей. Причиной этого служит необходимость разбиения всего приложения на части (модули) с их последующей сборкой. Выбор архитектуры обеспечивает требуемую модульность. Разработчики, которым поручается создание архитектуры (технические архитекторы), обычно являются самыми опытными в команде разработки.
5.1.3.
Цели выбора архитектуры.
Для конкретного проекта разработки программного обеспечения может быть несколько подходящих архитектур, из которых необходимо выбрать лучшую. Обычно бывает сложно удовлетворить все требования, поскольку архитектура может выполнять одно из требований и не выполнять другое. По этой причине всем требованиям необходимо присвоить приоритеты. Приведем пример списка основных целей разработки.
♦ Расширение.
Облегчение добавления новых свойств.
♦ Изменения.
Облегчение смены требований.
+ Простота:.
♦ простота понимания;.
♦ простота реализации.
♦ Эффективность:.
♦ достижение высокой скорости: выполнения и (или) компиляции;.
♦ достижение малого размера: объектного кода и (или) исходного кода.
Расширение определяет степень, в которой архитектура должна поддерживать.
добавление новых возможностей в приложение. Чаще всего чем лучше архитектура приспособлена к расширению, тем более сложную структуру она имеет и больше времени требуется на разработку. Расширяемость обычно требует введения более высоких абстракций в процесс. Например, мы можем пожелать, чтобы архитектура нашей видеоигры Встреча поддерживала не только эту игру (нижний уровень универсальности), но и вообще любую ролевую видеоигру. Универсальность дает множество преимуществ, но ее реализация требует больших затрат времени. Одной из важных задач при выборе степени универсальности является определение класса возможных расширений. Мы не можем проектировать в расчете на все возможные расширения. В связи с этим очень полезны необязательные и желательные требования, поскольку они показывают, в какую сторону будет направлено развитие приложения.
Разработка с расчетом на изменения преследует другие цели, хотя подразумевает применение тех же приемов проектирования, что и для обеспечения расширяемости. В данном случае мы хотим спроектировать архитектуру таким образом, чтобы она допускала изменение требований, например чтобы требование «игрок должен постоянно иметь полный контроль над своим персонажем» можно было заменить требованием «игрок время от времени случайным образом теряет контроль над своим персонажем».
Простота является целью проектирования при любых обстоятельствах. Простая архитектура, которая допускает расширения и изменения, является редкостью, и ее создание требует больших усилий. К другим критериям, используемым при выборе архитектуры, относятся экономия машинного времени и экономия памяти.
5.1.4. Декомпозиция.
После небольшой практики достаточно просто создавать маленькие программы. Большие приложения, однако, ставят перед разработчиками очень трудные задачи, решение которых довольно тяжело достигается на практике. Принципиальная проблема систем программного обеспечения — это их сложность. Сложность не в смысле количества строк кода, а в смысле их взаимосвязи. Очень хороший способ борьбы со сложностью — разбиение задачи на подзадачи, имеющие характерные свойства небольших программ. По этой причине декомпозиция (или модуляризация) является проблемой критической важности и одним из самых интересных этапов разработки. Разработчик первым делом должен представить, как приложение будет работать на высшем уровне, а затем разработать декомпозицию, соответствующую ментальной модели. Например, какие четыре или пять модулей будут реализованы в игре Встреча ? Или на пять или на шесть модулей разбить персональное финансовое приложение? Следующая проблема, которая встает перед разработчиком, — это декомпозиция уже полученных компонентов, и т. д. Этот процесс иногда называют рекурсивным проектированием.
Начнем с целей декомпозиции.
Связность внутри модуля — это сила взаимосвязей между элементами модуля. Сцепление характеризует степень взаимодействия модуля с другими модулями. Эффективная модульность достигается максимизацией связности и минимизацией сцепления. Такой способ дает возможность разбивать сложные задачи на более простые. Применение данного подхода к проекту моста иллюстрирует рис. 5.4. Шесть компонентов, полученных при декомпозиции моста, демонстрируют большую степень связности, сцепление же между ними очень мало.
Рис. 5.4. Связность и сцепление
Части (например, кирпичи) каждого компонента моста (например, опоры) взаимозависимы, то есть связность внутри каждого компонента высока. С другой стороны, каждый компонент зависит только от нескольких соседних компонентов, то есть сцепление между ними достаточно низкое. Например, опора связана только с двумя горизонтальными блоками моста. Возьмем другой пример — железную ферму (см. рис. 5.4). Здесь все компоненты имеют одну общую точку, и следовательно, сцепление между этими компонентами очень высоко.
Малое сцепление в совокупности с большой связностью очень важны при проектировании приложений ввиду постоянного процесса внесения изменений в проекты. Сравним жизненный цикл приложения и моста. Изменения в приложении во много раз более вероятны, чем в конструкции моста. Архитектуры с малым сцеплением и большой связностью более приспособлены для модификации, поскольку изменения в таких архитектурах имеют наиболее локальный эффект. Однако создавать такие архитектуры достаточно сложно.
Количество модулей высокого уровня должно быть невелико. Обычно рекомендуемое количество (норматив) — 7+2, но в силу специфики некоторых проектов это число может сильно варьироваться. Разница между мелкомасштабными и крупномасштабными проектами измеряется количеством уровней вложения в модулях. В крупномасштабных приложениях модули высокого уровня разбиваются на подмодули, те в свою очередь разбиваются на под-подмодули и т. д. Норматив 7±2 применяется для каждой такой декомпозиции.
В качестве примера рассмотрим декомпозицию игры Встреча. Один вариант — разбить все части игры на четыре модуля.
+ Окружающая среда, в которой происходит действие игры (зоны, соединения и т. д.).
♦ Механизм управления игрой (счетчики, реакции на события и т. д.).
♦ Участники игры (персонаж игрока, другие персонажи и т. д.).
♦ Артефакты, задействованные в игре (мечи, книги, щиты и т. д. — они появятся в будущих версиях игры).
Все эти модули обладают достаточной связностью. Например, герои игры достаточно интенсивно взаимодействуют между собой. С другой стороны, сцепление этих модулей сильнее, чем нам бы хотелось. Например, при встрече персонажей задействуются и окружающая среда, и механизмы управления, и сами персонажи, и артефакты.
В качестве другого примера рассмотрим декомпозицию персонального финансового приложения.
•♦• Счета (проверка, сохранение и т. д.).
♦ Оплата счетов (электронная, чеком и т. д.).
♦ Глобальные отчеты (общие активы, задолженность и т. д.).
♦ Займы (автомобиль, образование, дом и т. д.).
♦ Инвестиции (акции, долговые обязательства и т. д.).
Хотя эта декомпозиция привлекательна с точки зрения пользователя, она имеет большие недостатки как архитектурная декомпозиция. Например, счета имеют малую связность, поскольку они слабо взаимосвязаны. А связность модулей здесь довольно велика. Например, при выплате задолженности задействуются счета, оплата счетов, займы и. возможно, отчеты.
Существует следующая альтернатива этой декомпозиции.
♦ Интерфейс (пользовательский интерфейс, коммуникационный интерфейс, отчетность и т. д.).
♦ Поставщики (арендодатель, ссуды, коммунальные услуги и т.д.).
+ Активы (проверка счетов, акции, долговые обязательства и т. д.).
Совершенная архитектура — цель достойная, но труднодостижимая. Программирование — это не единственная область инженерии, в которой сложно получить безупречную модульность. В [100] Шнаерсон показал, что, несмотря на все попытки General Motors разбить на модули проект своего первого электрического автомобиля, факторы соответствия форме (требования соответствия деталей автомобиля ограниченному пространству) порождали высокий уровень сцепления между компонентами.
Подведем итоги этого раздела.
ОДИН ИЗ СПОСОБОВ НАЧАТЬ ВЫБОР БАЗОВОЙ АРХИТЕКТУРЫ -.
1.
Разработать ментальную модель приложения на высоком уровне, как если бы это было маленькое приложение. Например, персональное финансовое приложение получает или выдает деньги в любом порядке под управлением интерфейса пользователя.
2.
Выполнить декомпозицию на требуемые компоненты. Поиск высокой связности и низкого сцепления. В частности, для персонального финансового приложения выполняется декомпозиция на Активы, Поставщики и Интерфейс.
3.
Повторить этот процесс для компонентов.
Список уже известных архитектур приведен далее в этой главе.
Детали_.
Эта часть главы может быть полностью освоена после прочтения последующих глав. Однако понимание основной идеи этой части необходимо для производства качественных программных продуктов.
5.2. Модели, каркасы и образцы проектирования.
Декомпозиция всего проекта на компоненты является существенным шагом, но нам еще предстоит гораздо большая работа по созданию архитектуры. Для начала нам необходимо согласовать варианты использования, классы, переходы состояний и декомпозицию. Мы называем эти проекции моделями, и они будут рассмотрены в разделе 5.2.1.
При создании модели классов целесообразно разрабатывать и использовать уже существующее программное обеспечение, которое образует базис для семейства сходных приложений. Такое семейство, называемое каркасом, рассмотрено в разделе 5.2.3.
Детальное проектирование — это полный объем работ по проектированию, исключая архитектуру и реализацию. Оно содержит определение классов, связывающих классы предметной области и классы архитектуры. Это будет темой обсуждения в главе 6.
Вместо того чтобы «изобретать велосипед», мы стараемся использовать разработки, уже доказавшие свою эффективность в предыдущих приложениях. Образцы проектирования — это шаблоны взаимодействующих классов и методов, которые уже показали свое значение для многих приложений. В качестве примера можно взять набор классов, реализующих дерево объектов. Образцы используются как на уровне архитектуры (раздел 5.2.5), так и на уровне детального проектирования (глава 6).
Далее в качестве упражнения мы разработаем каркас классов для игры Встреча, общий для всех ролевых игр. Основываясь на этом каркасе, в этой главе мы представим архитектуру игры Встреча. Для завершения проектирования данной игры классы предметной области, полученные на этапе анализа требований, необходимо подготовить к работе с классами архитектуры. Мы сделаем это путем добавления классов детального проектирования (глава 6).
5.2.1. Использование моделей.
Обычно нам необходимо описать приложение с нескольких точек зрения. Это можно сравнить с архитектурой дома, которая требует нескольких проекций, таких как план расположения участков земли, вертикальный и фронтальный виды, план водопровода и т. д. В мире программирования проекции называются моделями. За последние годы в этой области появилось множество прекрасных разработок. Различные модели проектируемого приложения показаны на рис. 5.5 и 5.6. Многие обозначения взяты из USDP, рассмотренного в [64] и [75].
Рис. 5.5. Модели, рассматривающие приложение с разных точек зрения
Рис. 5.6. Модели и их части
Модель вариантов использования представляет собой коллекцию вариантов использования. Они поясняют, что именно приложение должно делать. Начальная версия вариантов использования подходит для использования в качестве С-требований (иногда их называют «деловые варианты использования» или «варианты использования предметной области»), В процессе реализации проекта они приобретают свойства диаграмм последовательности специального вида. Их реализуют в виде конкретных сценариев, которые затем используются при тестировании. В качестве примера варианта использования для игры Встреча можно взять случай встречи с внешним персонажем, рассмотренный в примере к этой главе.
Модель классов. Мы уже рассмотрели достаточно много моделей классов (диаграмм классов). Модель классов объясняет построение блоков, из которых будет сформировано приложение. Модели классов зачастую называют объектными моделями. Внутри модели классов мы можем показать методы и атрибуты.
Модель компонентов является коллекцией диаграмм потоков данных. Они объясняют, каким образом приложение будет работать в терминах перемещения данных. Модель компонентов игры Встреча в упражнениях будет включать диаграмму потоков данных о характеристиках между внешним персонажем и персонажем игрока при их встрече.
Модель переходов состояний представляет собой коллекцию диаграмм переходов состояний. Модель переходов состояний определяет момент времени, в который приложение осуществляет свою работу. В игре Встреча мы уже разработали такую модель (диаграмма переходов состояний в главе 3). Она показывает реак-.
*.
цию игры на появление других персонажей, а также на запросы пользователя установить характеристики, закрыть окно, выйти из игры и т. д.
Внутри каждой модели мы прорабатываем уровни детализации, количество которых растет. В зависимости от объемов работы мы можем итеративно применять деление на уровни детализации внутри каждой из моделей. Архитектура приложения зачастую выражается в терминах одной из моделей и поддерживается остальными моделями. USDP включает в себя модель реализации. которая должна быть выполнена с учетом организации кода. В каждой архитектуре есть по меньшей мере одна модель классов, способная реализовать эту архитектуру.
5.2.2. Унифицированный язык моделирования (UML).
В моделях классов, которые будут рассматриваться далее, мы будем широко использовать унифицированный язык моделирования — UML (Unified Modeling Language) [15,95]. Мы уже использовали нотацию UML для вариантов использования и диаграмм последовательности. Основы нотации UML для моделей классов показаны на рис. 5.7 и 5.8. Далее мы рассмотрим эти рисунки подробнее. На самом высоком уровне модели классов для компонентов архитектуры в UML используется термин пакет. Пакеты — это коллекции классов. Пакетами иногда называют коллекции классов Java, и эти термины довольно точно соответствуют друг другу. Пакеты Java транслируются в каталоги, подпакеты — в подкаталоги и т. д. Вообще говоря, UML позволяет помещать в пакеты любые материалы, связанные с приложением, в том числе код, результаты проектирования, документацию и пр.
Рис. 5.7. Пакеты, абстракция, наследование, атрибуты и ориентация в UML
Рис. 5.8. Агрегация и зависимость в UML
Абстрактные классы не имеют экземпляров, то есть объектов (на рисунках имена таких классов выделены курсивом). Агрегация, помеченная ромбом, указывает на то, что объекты одного класса являются вложенными в другой класс. Число на полюсе агрегации означает количество агрегированных объектов. Например, число 1 на полюсе отношения DerivedClass/AggregatedClass означает, что каждым объектом класса DerivedClass агрегируется один объект класса AggregatedClass. Вместо числа может быть указан интервал, например 3.7. Символ * означает некоторое число агрегированных объектов. Зависимость обозначается стрелкой с пунктирной линией. Обычно она означает ссылку метода зависимого класса на другой класс.
5.2.3. Каркасы.
Каркас — это коллекция классов, используемых в нескольких различных приложениях. Часто классы внутри каркаса взаимосвязаны. Они могут быть абстрактными и использоваться через наследование.
Интерфейс прикладного программирования (API), реализованный в Java, является примером полезных каркасных пакетов. Он показал, насколько сообщество разработчиков нуждается в богатой коллекции каркасов для выполнения своей работы. Базовые пакеты Java API могут быть использованы в огромном количестве разнообразных приложений. Связывание пакетов приложений с каркасными пакетами осуществляется при помощи агрегации и (или) наследования. Рассмотрим, например, использование пакета Java Windowing Toolkit (awt): вместо того чтобы модифицировать awt, мы создаем классы графического интерфейса пользователя, которые унаследованы от awt или агрегируют объекты awt как атрибуты.
Существует мнение, что каркасы следует создавать только в том случае, если они, так же как и Java API, будут использоваться большим количеством приложений. Однако разработка частичного каркаса параллельно с приложением дает множество преимуществ, даже когда нет уверенности в том, что этот каркас будет пригоден для большого числа приложений. Этот частичный каркас нередко служит неизменным абстрактным уровнем, который наследуют классы многих приложений.
В качестве примера создадим каркас, который можно использовать в игре Встреча. Выполним декомпозицию игры как приложения. Варианты декомпозиции легко получить, группируя классы предметной области, полученные при анализе требований (Зона, ПерсонажВстречи, ИграВстреча, Контакт, ОкноКонтакта, ВнешнийПерсонаж, ПерсонажИгрока и ОкноХарактеристикИгрока).
Каждый из этих классов должен подходить для пакета приложения, и каждый пакет приложения должен использовать один или более пакетов каркаса. Например, согласно этому принципу, один каркасный пакет Персонаж может определять участие героев в игре Встреча. Также может быть создан пакет приложения ПерсонажВстречи, использующий Персонаж и содержащий классы для персонажей класса ИграВстреча (рис. 5.9). Декомпозиция ролевой игры на пакеты каркаса и пакеты приложения Встреча показана на рис. 5.10.
Рис. 5.9. Уровни для ролевых видеоигр
Каркасный пакет Персонажи определяет и персонажи, управляемые игроком, и персонажи, управляемые приложением. Каркасный пакет Артефакты состоит из элементов, не принадлежащих никаким другим пакетам. Необходимость пакета АртефактыИгры на этой стадии еще не совсем ясна. Возможно, еще рано рассуждать о том, должны ли, например, аксессуары рассматриваться как часть уровня или щиты содержаться в пакете Персонаж.
Рис. 5.10. Пакеты архитектуры для ролевых видеоигр, построенные на основе классов предметной области
Мы стремились получить высокую связность и низкое сцепление в приложении Встреча, группируя логические сущности (персонажи, игра, управление и отображение). Если бы мы группировали классы так, как они появляются на мониторе (персонажи внутри зоны), то пакеты имели бы неприемлемо низкую связность, поскольку они не являются сильно связанными классами. Сцепление в этом случае оказалась бы очень высоким, поскольку мы должны были бы обеспечить доступ и к зоне, и к персонажу (а это очень разные сущности) извне пакета.
Путь, который приводит нас к модели классов, представлен на рис. 5.11. Классы предметной области получены как результат анализа требований. Классы каркаса получены из разработки архитектуры для приложений, подобных тому, которое мы проектируем. Таким образом, классы каркаса либо являются частью ранее существовавшего пакета, либо разработаны, как в процессе анализа архитектуры. Наконец, оставшиеся классы (проектные классы) добавлены для завершения проектирования. Завершенное проектирование состоит из всех классов предметной области, всех классов проектирования и некоторых каркасных классов (рис. 5.12). (Обычно мы не используем все каркасные классы.) Каркасные классы, использованные в проекте, являются частью архитектуры приложения. Все классы предметной области — это часть детального проектирования, поскольку они специфичны для каждого приложения, получены из требований и не относятся к архитектуре. В качестве примера возьмем класс ПерсонажИгрока из упражнения. По сути, он является отдельным классом, описывающим поведение персонажа. Некоторые из проектных классов создаются в процессе проектирования архитектуры, другие — в процессе детального проектирования.
Рис. 5.12. Сравнение модели классов с архитектурой и детальным проектированием
5.2.4. Классификация архитектур.
Шоу и Гарлан [34] классифицировали архитектуры программного обеспечения с точки зрения практики. Другими словами, они собрали вместе образцы программного обеспечения для различных архитектур. Их классификация, немного адаптированная, показана ниже. ♦ Архитектуры потоков данных.
♦ Последовательные пакеты.
♦ Каналы и фильтры.
+ Независимые компоненты.
♦ Параллельные взаимодействующие процессы.
♦ Клиент-серверные системы.
♦ Системы, управляемые событиями.
♦ Виртуальные машины.
♦ Интерпретаторы.
♦ Системы, основанные на правилах.
♦ Репозиторные архитектуры.
♦ Базы данных.
♦ Гипертекстовые системы.
♦ Доски объявлений.
♦ Уровневые арихитектуры.
Большинство из этих архитектур подробно рассматриваются в разделе 5.3. Существует широкий спектр проблем, требующих программного решения, но существует также широкий спектр архитектур, необходимых для их решения. Возможно, одна из архитектур, определенных Шоу и Гарланом, будет соответствовать вашей задаче или хотя бы подскажет идею декомпозиции.
5.2.5. Образцы проектирования I: введение.
Образец проектирования — это найденная опытным путем комбинация компонентов, обычно классов или объектов, которая решает определенные общие проектировочные задачи. Воспользуемся аналогией с архитектурой дома и рассмотрим задачу проектирования уединенного здания на обширной территории. Архитектура Ранчо (одноэтажный дом) полностью удовлетворяет этим требованиям. Заметьте, что Ранчо указывает на общую идею проектирования, предусматривающую множество реализаций, и совсем fie является неизменным множеством планов дома.
Гамма представил вниманию сообщества разработчиков образцы проектирования в теперь уже классической книге [33]. Гамма рассматривает двадцать три образца проектирования, разделяя их на структурную, креациопную и поведенческую категории. Структурные образцы проектирования имеют дело со способами представления объектов (такими, как деревья или связные списки). Они удобны во многих случаях, поскольку позволяют пользоваться множеством объектов как единым целым. Креационный образец проектирования связан со способами создания сложных объектов, таких как лабиринты и деревья. Поведенческий образец проектирования позволяет нам следить за поведением объектов, например, выдавая отчет о коллекции объектов в определенном порядке. Хотя существует гораздо больше образцов проектирования, чем приведено в [33], мы все же сосредоточим наше внимание на применении рассмотренных образцов проектирования.
Образцы проектирования могут быть применены на уровне архитектуры и (или) на уровне детального проектирования. Список образцов проектирования, особенно полезных на уровне архитектуры, представлен в табл. 5.1. Далее в этой главе мы постепенно раскроем их смысл.
Таблица 5.1. Обобщение образцов проектирования архитектуры
Цели проектирования
Образец проектирования
См. раздел
Обеспечить интерфейс для множества объектов различных классов
Facade
5.3.2.1
Заставить объект вести себя соответственно его текущему состоянию
State
5.3.2.3.
Инкапсулировать пути «посещения» объектов в коллекции так, чтобы клиентский код мог выбирать пути «посещения» во время выполнения
Iterator
5.3.4.1
Обеспечить реакцию нужных элементов на изменения в источнике данных
Observer
5.3.2.2.1
Интерпретировать выражения на языке формальной грамматики
Interpreter
5.3.4.1
Применение каждого образца проектирования зависит от клиента — кода, который нуждается в сервисе, предоставляемом образцом проектирования. Клиент ссылается на точку входа образца проектирования (обычно это метод класса внутри образца). Кроме того, обычно подразумевается и третий тип кода, который можно назвать установочным кодом. Он устанавливает состояние образца проектирования. Установочный код не предназначен для повторного использования и предоставляет клиентам возможность легко взаимодействовать с образцом проектирования. В частности, для работы с образцом проектирования клиенту следует знать как можно меньше о его структуре и внутренней работе.
5.2.6. Компоненты.
Во второй половине 90-х годов сильно возрос интерес к понятию «компонент». Компонентами являются повторно используемые объекты, которые не требуют знания программного обеспечения, использующего их. Наглядным примером технологии компонентов могут служить объекты СОМ и Java Beans. Компоненты могут быть объектами в обычном понимании объектно-ориентированного программирования, но с небольшими поправками на обеспечение их автономности. Одни компоненты используются другими с помощью агрегирования и взаимодействуют в основном с помощью событий.
Обобщение связей между каркасом, архитектурой, детальным проектированием, моделями и образцами проектирования показано на рис. 5.13. Образцы проектирования могут быть использованы и на уровне каркаса, и внутри проектных классов на уровне архитектуры, и на уровне детального проектирования.
Обычно никто не пытается применить образцы проектирования внутри классов предметной области, поскольку последние разработаны индивидуально и соответствуют непосредственно множеству требований. Как далее будет показано, образцы проектирования зачастую требуют введения классов, не являющихся классами предметной области, например абстрактных классов.
Рис. 5.13. Связи между каркасами, архитектурой, проектированием и моделями
5.3. Типы архитектур и их модели.
Разработчик программного обеспечения создает ментальную модель, описывающую работу приложения, ограничиваясь при этом пятью-семью компонентами (весьма приблизительная оценка). Результат проектирования, конечно, определяется в основном приложением, однако многое может быть позаимствовано из ранее разработанных архитектур, так же как в проект подвесного моста войдет множество преимуществ ранее разработанных подвесных мостов. В этом разделе мы подробно рассмотрим архитектуры, классифицированные в [34], и укажем образцы проектирования, которые могут помочь в реализации этих архитектур.
Сначала мы обобщим встречающиеся типы архитектур в табл. 5.2, а затем поясним большинство из них.
Таблица 5.2. Типы архитектур (по классификации Гарлана и Шоу).
Категория Подкатегория Часто применяемые Примечания.
образцы проектирования
Поток данных
Последовательность пакетов
Может применяться образец Decorator ([33])
Независимые компоненты
Каналы и фильтры
Параллельные.
взаимодействующие.
процессы
Observer.
(раздел 5.3.2.2.1)
Клиент-серверные системы
Facade.
(раздел 5.3.2.1)
Системы,.
управляемые.
событиями
State.
(раздел 5.3.2.3), Observer
Виртуальные машины
Интерпретаторы
Interpreter (раздел 5.3.3)
Системы на основе правил
Объяснения правил в [43]
Репозиторные архитектуры
Базы данных
Observer, Iterator (раздел 5.3.4.1)
Гипертекстовые системы
См. Decorator в [33]
Доски объявлений
Определение досок объявлений в [29]
Уровневые архитектуры
Большинство образцов проектирования состоят из абстрактного и неабстрактного уровней
5.3.1. Архитектуры, основанные на потоках данных.
Для представления некоторых приложений наилучшим образом подходят потоки данных между процессами обработки данных. Такое представление иллюстрируруют диаграммы потоков данных (DFD — Data Flow Diagram). Каждый процесс обработки данных на диаграмме потоков данных проектируется независимо от других. Данные приходят из различных источников, например от пользователя, и, в конечном итоге, возвращаются к пользователю или в приемники данных, такие как база данных счетов. Элементы диаграммы потоков данных были рассмотрены в разделе 3.3.3. Диаграмма потоков данных для банковского приложения представлена на рис. 5.14.
Данные передаются от пользователя к процессу Получение депозита. Далее процесс Получение депозита посылает номер счета и размер депозита процессу, проверяющему данные на совместимость. Если данные корректны, то они могут быть отправлены процессу, создающему транзакцию, и т. д. Например, процесс Создание транзакции запроса сам по себе может быть разбит на более подробные диаграммы потоков данных. Эта тема будет рассмотрена в главе 6.
Рис. 5.14. Частичная диаграмма потоков данных для банковского приложения
Архитектура потоков данных, относящаяся к архитектурам типа каналы и фильтры, представлена на рис. 5.15. Такие архитектуры потоков данных состоят из процессов (фильтров), способных в любой момент времени принять потоки как входную информацию (последовательность данных единообразного вида). Каждый фильтр должен проектироваться независимо от других. Такая архитектура может быть легко реализована с помощью каналов Unix.
Архитектура каналов и фильтров имеет явное преимущество — модульность. Пример приложения с такой архитектурой показан на рис. 5.16. Это приложение обслуживает транзакции счетов, приходящие в случайные моменты времени по линиям связи. Архитектура содержит шаг для регистрации транзакции в случае отказа системы. Функция снятие() получает в качестве входного параметра символьную строку вида ДжонДоуНомерСчета12345Сумма1$3500.00 или ДжонДоуНомерСчета12345Сумма1$3500.00, а также банковский адрес вида НомерБанка987б. Процессы, изображенные в эллипсах, ожидают появления значения на входе, для того чтобы начать свою работу.
В общем случае не существует унифицированного способа изображения диаграммы потоков данных для моделей классов. Однако функциональные модули диаграммы потоков данных могут иногда указывать прямо на методы классов. Примером является диаграмма на рис. 5.16.
Рис. 5.15. Архитектура каналов и фильтров
Рис. 5.16. Пример выбора потоков данных в архитектуре каналов и фильтров
Благодаря росту объемов распределенных вычислений увеличивается количество приложений с потоко-ориентированными вычислениями. Причиной является тот факт, что передача данных зачастую осуществляется в виде форматированных символьных строк. Такой способ передачи данных реализован, например, в удаленном вызове метода (RMI — Remote Method Invocation) в Java. RMI преобразует передаваемые объекты в символьные строки. Кроме того, часто вводвывод также бывает реализован с применением потоков, поэтому использование ввода-вывода в таких языках, как Java, как правило, аналогично процессамфильтрам.
В частном случае, когда на вход фильтров подается информация только в виде пакетов, результатом будет поток данных в виде последовательности пакетов. В качестве примера рассмотрим банковское приложение, вычисляющее, сколько денег свободно для выдачи ссуд под заклад и для выдачи необеспеченных ссуд. Диаграмма потоков данных этого приложения представлена на рис. 5.17. Эта диаграмма пакетно-последовательная, поскольку функции используют для своей работы фактически всю поступающую информацию. Например, подсчитывая фонды для ссуд под заклад, функции используют информацию практически обо всех счетах. Этот пример существенно отличается от предыдущего примера транзакции (см. рис. 5.16), поскольку там мы имели множество (потенциально неограниченное) транзакций, использующих выделенные данные из своих источников.
Рис. 5.17. Пример архитектуры пакетного последовательного потока данных
Рисунок 5.17 также иллюстрирует одно отображение на классовую модель, в которой функции диаграммы потоков данных реализованы в виде методов класса Банк. Пакеты выполняются путем вызова соответствующих методов этого класса.
В течение десятилетий потоки данных являлись наиболее общим способом отображения архитектур, и можно с уверенностью сказать, что они не потеряют актуальности и в будущем. Для разработчиков вполне естественно представлять себе, что данные путешествуют от одного узла к другому и обрабатываются в каждом узле. Недостатком диаграмм потоков данных является то, что их отображение на программный код не вполне ясно, причем независимо от того, идет ли речь об объектно-ориентированном коде, или нет.
Мы воспользуемся моделью потоков данных еще раз при рассмотрении детального проектирования в следующей главе.
5.3.2. Независимые компоненты.
Архитектура независимых компонентов состоит из компонентов, работающих параллельно (по крайней мере, теоретически) и время от времени общающихся друг с другом. Возможно, самый очевидный пример можно найти в World Wide Web, где тысячи серверов и миллионы браузеров все время работают параллельно и иногда общаются между собой.
5.3.2.1. Клиент-серверная архитектура и образец проектирования Facade.
В отношениях клиент-сервер серверные компоненты обслуживают нужды клиентов с помощью запросов. Клиент-серверные отношения имеют огромное преимущество — малое сцепление между компонентами. Такие отношения применяются в разработке программного обеспечения в основном в тех случаях, когда в реализации участвует более одного человека: вполне естественно разделить пакеты классов на части для каждого разработчика из группы. При этом разработчики обычно используют сервисы классов, за которые отвечают другие разработчики. Другими словами, пакеты разработчиков часто связаны такими же отношениями, что и серверы с клиентами. Обычно для этой ситуации характерна одна проблема — различные уровни готовности компонентов в процессе разработки.
Компонент работает как сервер более эффективно в случае, если он имеет узкий интерфейс. «Узкий» означает, что интерфейс (по сути, набор функций) не имеет излишеств, собран в одном месте и четко определен. Образец проектирования Facade предоставляет пакетам классов именно такой интерфейс. Facade регулирует взаимодействие с другими объектами, предоставляя для использования только один объект и скрывая остальные. Этот открытый объект обычно единственный и является членом класса Facade. Структура образца проектирования Facade изображена на рис. 5.18.
Вызов, ссылающийся на объект внутри пакета, преобразуется в вызов метода объекта Facade. Этот метод и ссылается на объект, требуемый вызовом.
Использование образца проектирования Facade для каждого пакета игры Встреча показано на рис. 5.19. Взаимодействие с персонажами игры должно осуществляться через одиночный объект класса РолиВстречи. Ссылки на элементы среды игры Встреча должны осуществляться через объект СредаВстречи.
Одна из трудностей, возникающих при применении образца проектирования Facade, связана с запретом на обращение к внутренним классам пакета. Например, пользователям пакета ПерсонажиВстречи обычно необходим доступ к отдельным персонажам игры, но они имеют доступ только к классу РолиВстречи. Более того, пользователи пакета ПерсонажиВстречи не могут даже ссылаться на класс ПерсонажВстречи, поскольку это противоречило бы принципу сокрытия данного класса. Это строгое ограничение. Однако если пакет каркаса (открытый) содержит класс ПерсонажИгры, то пользователи пакета ПерсопажиВстречи могут ссылаться на класс ПерсонажИгры. Обычно для управления доступом этого достаточно.
В частности, facade-miacc пакета ПерсонажиВстречи может иметь, например, такие открытые методы:.
ПерсонажИгры getMainCharacter();.
В этом случае клиентский код может выглядеть так:.
ПерсонажИгры главныйПерсонажИгрока = ПерсонажиВстречи.getMainCbaracterО :.
Все разрешенные действия, влияющие на главный персонаж игрока, управляются facade-объектом ПерсонажиВстречи.
Рис. 5.18. Структура образца проектирования Facade
Рис. 5.19. Архитектура и декомпозиция игры Встреча
Можно провести простую аналогию с подобным использованием образца проектирования Facade. Предположим, что вы звоните в фирму CampaLot Corp. с намерением купить палатку. Для этого вам необходимо знать о палатках хотя бы в общих чертах (это похоже на возможность ссылаться на каркасный класс Палатка). Но, кроме того, вы рассчитываете на интерфейс CampaLot Corp. (человека на другом конце телефонного провода — объект Facade), который обеспечит вам доступ к конкретному виду палаток.
Рассматривая применение образца проектирования Facade, следует учитывать дополнительные затраты на обеспечение доступа пользователей пакета к методам. Например, предположим, что у нас есть пакет Chemi stry. Он содержит класс Molecule, который в свою очередь содержит полезный метод atomicWeightO. Если мы используем facade-miacc MoT ecul eFacade, то Hoi ecul eFacade должен содержать открытый метод getAtomicWeightO, передающий управление методу atomicWeightO. Это требует дополнительной работы и согласования во время процесса разработки, но затраты на это с лихвой окупятся при возрастании модульности.
Клиент-серверные архитектуры были широко распространены в 80-х и 90-х годах прошлого века. Многие из них пришли на смену архитектурам «центральная машина — терминалы». Клиент-серверные архитектуры усложнились, стали более разнообразными. Некоторые из них являются трехуровневыми, в отличие от классических двухуровневых (клиент и сервер). Третий уровень находится между клиентом и сервером и отвечает за перенаправление данных и их преобразование. Обычно распределение по уровням таково: пользовательский интерфейс разрабатывается для клиентского уровня; управление процедурами — для среднего уровня; сами базы данных — для третьего. Средний уровень может быть общей шиной данных, примером которой является стандарт CORBA (Common Object Request Broker — Общая архитектура посредника запросов к объектам). Возможен альтернативный вариант, когда средний уровень функционирует на основе бинарного стандарта, например СОМ. Наконец, World Wide Web можно рассматривать как целое поколение клиент-серверных архитектур, в котором архитектуру один сервер — десятки клиентов заменила архитектура один сервер — миллионы клиентов.
5.3.2.2. Архитектура параллельных взаимодействующих процессоров.
Еще один тип архитектур независимых компонентов был определен Шоу и Гарланом как архитектура параллельных взаимодействующих процессов. Такая архитектура характеризуется тем, что в ней одновременно запускаются несколько процессов (в разных потоках, выражаясь в терминах Java). Элементы этой архитектуры используются в игре Встреча-, внешний персонаж Фредди перемещается независимо из одной зоны в другую параллельно с выполнением программы самой игры. Его поток «общается» с другими потоками, когда бы Фредди ни оказался в одной зоне с персонажем игрока.
Нотация UML, выражающая параллелизм, была рассмотрена в главе 4. С помощью этой нотации мы представили архитектуру банковского приложения (рис. 5.20), которое предназначено для обработки множественных транзакций, появляющихся последовательно на банкомате.
Требование: управлять действиями банкомата
Рис. 5.20. Пример архитектуры параллельных взаимодействующих процессов
Когда клиент п пользуется банкоматом, создается объект клиент п (1 на рис. 5.20). Этот объект создает поток или параллельный процесс сессия т (2), что на диаграмме отмечается половинной стрелкой. Процесс сессия т извлекает объект класса Счет, например проверка клиента п (3). Затем клиент выполняет депозитную транзакцию над проверяющим объектом (4). Параллельно с этим создаются другие объекты Клиент, такие как клиент п+1, и исполняются в других потоках, например в сессии k.
Если в приложении необходимы параллельные процессы, наилучшим выбором является архитектура параллельных взаимодействующих процессов. Эта архитектура может быть использована для схем, координирующих концептуально независимые задачи. В классической книге [25] Дийкстра показал, что рассмотрение процесса как комбинации параллельных составляющих может зачастую упростить разработку. Примером тому является имитация клиентов банка. Традиционно такие имитации разрабатывались без параллелизма — они просто сохраняли и обрабатывали события. Однако проектирование в подобных случаях можно упростить, если рассматривать действия каждого клиента независимо. Такая архитектура параллельно взаимодействующих процессов имеет существенное преимущество — она более всего соответствует действиям, которые имитируются.
Более подробно параллельные взаимодействующие процессы в контексте Java рассмотрены в [76].
5.3.2.2.1. Образец проектирования Observer.
Архитектура независимых элементов зачастую складывается из источника данных и некоторого количества клиентов. Данные клиентов должны обновляться каждый раз, когда в источнике происходит какое-либо изменение. Например, предположим, что штаб-квартира Международной корпорации гамбургеров хранит на своем сервере данные о продаже гамбургеров по всему миру. Распределенными клиентами для этих данных являются Главное управление компании, отдел маркетинга и отдел продаж. Данные постоянно изменяются, и каждому из клиентов необходимо обновлять отображение данных в соответствии с его требованиями и запросами. Например, гистограмма в Главном управлении компании обновляется всякий раз, когда происходит 5-процентное изменение, для отдела маркетинга отображается новая круговая диаграмма, когда происходит изменение по крайней мере на 1 %, а в отделе продаж данные, отображаемые в таблицах, обновляются при любом изменении.
В качестве архитектуры, способной удовлетворять этим требованиям, можно использовать образец проектирования Observer. Стороны, которым требуется обновлять данные, называются наблюдателями и являются подклассами одного абстрактного класса, который мы назовем Observer. Схема образца проектирования Observer изображена на рис. 5.21. Проследим за порядком работы класса Observer.
1.
Клиент ссылается на фиксированный объект, требуя извещения для наблюдателей. Клиентский объект может представлять собой процесс, запрограммированный отмечать изменения данных, или работающее по расписанию задание. В такой модели, как мы видим, объект-клиент сообщает объектуисточнику, чтобы тот выполнил свою функцию извещения notifyO.
2.
Метод извещения notifyO сообщает всем объектам-наблюдателям, что им необходимо выполнить функцию обновления updateO.
3.
Способ выполнения метода updateO зависит от конкретного наблюдателя (ConcreteObserver), которому он принадлежит. Функция updateO сравнивает состояние объекта ConcreteObserver (по значениям переменных) с состоянием центрального источника данных на сервере, а затем решает, менять ли значения своих переменных, чтобы привести их в соответствие с источником данных, или выполнить другие действия, например создать новое отображение.
Примененив схему образца проектирования Observer для решения задачи с Международной корпорацией гамбургеров, мы получаем архитектуру, изображенную на рис. 5.22. Преимуществом этого образца проектированя является то, что он получил широкое признание (в языке Java, например, существуют даже такие классы: Observer (Наблюдатель) и Observable (Наблюдаемое)). Другое преимущество состоит в том, что такая модель позволяет ликвидировать наблюдатели и создавать новые, не нарушая взаимодействия остальных наблюдателей. Недостаток образца проектирования Observer проявляется в том случае.
если очень немногим из наблюдателей необходимо реагировать на происходящие изменения: в таком случае многочисленные извещения становятся причиной бесполезной траты ресурсов. Кроме того, эту модель также нежелательно применять, когда политика обновления предлагается и определяется более естественно самими наблюдателями или когда она значительно отличается для разных наблюдателей.
Рис. 5.21. Образец проектирования Observer
Рис. 5.22. Применение образца проектирования Observer для Международной корпорации гамбургеров
5.3.2.3. Архитектуры событийно-управляемых систем и образец проектирования State.
Рассмотрим событийно-управляемые системы — третий тип архитектуры независимых компонентов, предложенной Шоу и Гарланом. В данной архитектуре приложения представляются состоящими из набора компонентов, каждый из которых находится в состоянии ожидания, пока не произойдет воздействующее на него событие. К таким системам относятся многие современные приложения. Например, текстовый редактор Word ждет, пока пользователь не щелкнет на значке или на пункте меню. Далее происходит соответствующая реакция: сохранение файла, изменение шрифта и т. п. Системы, управляемые событиями, часто строятся на основе модели переходов состояний (см. главу 3).
Если по сути поведение системы заключается в смене состояний и задан набор состояний, через которые она переходит, то при проектировании подобной системы следует подумать об использовании образца проектирования State. Например, общее требование для игры Встреча мы описали в виде диаграммы переходов состояний (см. рис. 3.11). Игра Встреча может находиться в таких состояниях, как Настройка, Ожидание, Установка характеристик, Оповещение, Контакт, а также некоторых других. Наша модель проектирования эффективно отражает данное поведение. Эта модель также должна быть способна усваивать новые состояния и уметь управлять действиями, когда проектирование игры подходит к стадии завершения; при этом не должна нарушаться уже существующая модель. Для этого мы используем образец проектирования State.
С помощью образца проектирования State можно решить следующую задачу: как использовать объект, не зная его состояния. В контексте игры Встреча нам необходимо написать код, управляющий действиями мыши, который, однако, не должен содержать ссылок на возможные состояния игры и на определенные побочные действия мыши. Это позволит нам добавить к игре новые игровые ситуации, не нарушая имеющийся управляющий код.
Подойдем к ситуации в самом общем смысле: необходимо использовать метод doRequestO объекта target класса Target. При этом поведение метода doRequestO зависит от состояния, в котором находится объект target. Можно решить эту задачу так: ввести новый класс TargetState и дополнить класс Target свойством targetState типа TargetState. Требуется, чтобы свойство targetState всегда должным образом отражало текущее состояние объекта target, который является объектом соответствующего подкласса класса TargetState (рис. 5.23).
Метод doRequestO просто вызывает по очереди методы targetState.handleRequestO, таким образом, вызов метода doRequest() транслируется свойством виртуальной функции в конкретную версию функции handleRequestO, соответствующую состоянию объекта target. Все это происходит невидимо для клиента, вызывающего метод doRequest(). Другими словами, клиенту не требуется знать состояние объекта target.
Рис. 5.23. Структура образца проектирования State: поведение метода doRequest() зависит от состояния объекта target
Применение образца проектирования State к проекту Встреча для управления состояниями и действиями видеоигры иллюстрирует рис. 5.24. Каркасный класс РолеваяИгра имеет свойство под названием состояние типа Состоя ниеИгры. Тип состояния (от которого зависит, к какому подклассу класса СостояниеИгры данное свойство принадлежит) определяет, что происходит, когда вызывается метод handleEventO в объекте типа РолеваяИгра. Программный код метода handleEventO в классе РолеваяИгра передает управление функции состояния handleEventO. Каждый подкласс класса СостояшеИгры выполняет функцию handleEventO по-своему. Например, если игра Встреча находится в состоянии Установка характеристик, а событие заключается в появлении внешнего персонажа, то окно, в котором пользователь задает значения характеристик персонажа, исчезает, потому что именно так запрограммирован метод handleEventO в классе Настройка. Кроме того, следствием определенного сочетания события и состояния является то, что Встреча, в соответствии с диаграммой переходов состояний, переходит в состояние Контакт. Такой переход осуществляется с помощью кода, например, такого вида:.
ИграВстреча.setState(new Ионтант());.
Когда в игре произойдет следующее событие, будет выполнена функция handleEventO класса Контакт, поскольку сейчас игра находится в состоянии Контакт.
Архитектура образца проектирования State особенно выгодна, когда велика вероятность того, что в будущем потребуется добавить в систему новые состояния. Недостатком этого образца проектирования является то, что он не способен решить вопрос: какое состояние необходимо установить после того, как будет выполнена функция handleEventO. Альтернатива образца проектирования State заключается в использовании простой таблицы «состояние—действие», записи которой указывают, какие действия следует совершить, если приложение находится в данном состоянии и происходит данное событие.
Рис. 5.24. Применение образца проектирования State к ролевой игре и к игре Встреча
Пример работы с образцом проектирования State приводится в разделе 5.5.2. где также перечислены все «за и против» его применения. Независимо от того, применяется или нет образец проектирования State, архитектуры, ориентированные на состояние, с успехом используются для многих приложений (рекомендуем уделить особое внимание работам [Sh2]). Приложениям, расчитанным на работу в реальном времени, таким как приложение маршрутизации для мобильного телефона, использование архитектуры состояний дает особые преимущества.
5.3.3. Виртуальные машины.
Архитектура виртуальных машин рассматривает приложение как программу, написанную на специальном языке. Из-за того, что должен быть реализован интерпретатор этого языка, эта архитектура окупается, только если будут написаны несколько программ, генерирующих приложения.
В качестве примера архитектуры виртуальных машин рассмотрим приложение, обрабатывающее заказы определенных компьютерных систем одной сети. Обработка заказов выражается на специальном языке со следующей грамматикой:.
Program ::= assemble Program | price Program | System.
System ::= Computer | { System and System }.
Computer ::= { CPU & RAM }.
CPU ::= 260MHz | 300MHz | 400MHz.
RAM ::= 32MB | 64MB | 12MB.
Например, заказ на рис. 5.25 состоит из системы 260 МГц с оперативной памятью 64 Мбайт, которая соединена с системой, содержащей два компьютера: 400 МГц, 128 Мбайт RAM и 260 МГц, 32 Мбайт RAM.
Рис. 5.25. Пример «программы» для виртуальной машины Этот заказ выражается с помощью описанной грамматики следующим образом:
Assemble { {.
{ 260MHz & 64MB } }.
and.
{ { 400MHz & 128MB }.
and.
{ 260MHz & 32MB }.
}.
}.
Это выражение корректно с точки зрения описанной грамматики, как показано ниже.
Program.
assemble Program.
assemble System.
assemble { System & System }.
assemble { Computer & { System & System } }.
assemble { { CPU & RAM } & { Computer & Computer} }.
assemble { { 260MHz & 64MB } } & { { CPU & RAM } & { CPU & RAM } }.
assemble { { 260MHz & 64MB } } & { { 400MHz & 128MB } & { 260MHz & 32MB } }.
Вывод, выдаваемый виртуальной машиной, будет иметь вид:.
1 Construct system 1 as follows:.
2 Computer with 260MHz CPU and 64MB RAM.
3 Construct system 2 as follows:.
Construct system 3 as follows:.
Computer with 400MHz CPU and 128MB RAM Construct system 4 as follows:.
Computer with 260MHz CPU and 32MB RAM Connect System 3 and System 4 Connect System 1 and System 2.
Мы могли бы сделать вывод более удобным для использования, но в таком виде показанный нами вывод упрощает пример.
Преимущество использования архитектуры виртуальных машин в том, что вы можете свободно генерировать приложения, выражая их специальным языком. Например, следующий код мог бы быть еще одной программой, выдающей цену на основе обработанных инструкций:.
price assemble { ... }.
Очевидно, что можно сделать этот язык более мощным, а также заменить текстовое представление на графическое. Но и в этом случае применялась бы та же архитектура виртуальных машин.
Реализация полной виртуальной машины требует построения интерпретатора. В общем случае интерпретация вызывается операцией — назовем ее interpret О — над программой на нашем языке. Интерпретация простейшего одиночного элемента (например, CPU) в общем проста (например, это может означать требование вывести take CPU in the example). Проблема состоит в запуске interpretO при применении к более сложной программе.
Один из путей решения — использовать образец проектирования Interpreter. Гамма [33] указывает, что целесообразно использовать этот образец проектирования, когда грамматика мала и скорость не является важным фактором. Это справедливо для рассматриваемого нами примера. Образец проектирования Interpreter показан на рис. 5.26.
Рис. 5.26. Образец проектирования Interpreter
Объекты AbstractExpression являются либо объектами Terminal Expression, для которых функция интерпретации проста, либо объектами NonTerminalExpression. Последние агрегируют один или более объектов AbstractExpression. Функция interpretO, вызываемая для объектов NonTerminal Expression, выполняется, вызывая для каждого агрегированного объекта AbstractExpression свою версию interpretO.
В качестве примера рассмотрим архитектуру интерпретатора нашей грамматики, для простоты игнорируя расчет стоимости. Применяя образец проектирования Interpreter, мы получим диаграмму (рис. 5.27). Ограничимся сборкой только двух компонентов, то есть пусть класс System агрегирует только два объекта Component. Этот метод легко может быть расширен и для большего количества компонентов.
Рис. 5.27. Приложение, построенное на основе образца проектирования Interpreter
Заказ клиента из предыдущего фрагмента программы будет преобразован к следующему виду:.
assemblе( order ).
где order — это объект System:.
System order = new System (.
// Systeml задает параметр первого конструктора:.
new Computed 260, 64 ).
// System2 задает параметр второго конструктора; new System( new Computer ( 400. 128 ). new Computer (260, 32) ) ):.
Команда assemble( order ) объекта Component возвращается к методу assembleO объекта System. Он создает строку 1 в выводе и вызывает assembleO для каждого из двух своих параметров (см. рис. 5.27). Для первого параметра, объекта Computer, assembleO выдает строку 2. Затем появляется и строка 3. При вызове assembleO с третьим параметром выполняются действия над объектом System, который, в свою очередь, передает управление двум объектам Computer, и т. д.
Архитектура виртуальных машин очень полезна, если приложение состоит из процессов со сложными элементами (например, заказы), которые можно выразить с помощью грамматики. В качестве дополнительного примера можно привести приложение, выполняющее простое программирование на уровне пользователя на специальном языке. Обычный пользователь может написать, например, такой сценарий:.
Проверка баланса / добавить сумму на счет + вычесть дефицит из остатка; Сохранить отчет / с:Reports + стандартные заголовки + заменить «Ed» на «А1» Распечатать отчет / стандартные заголовки Отправить отчет по адресу
Архитектура виртуальных машин анализирует и интерпретирует такие сценарии.
5.3.4. Репозиторные архитектуры.
Архитектура, построенная главным образом вокруг данных, называется репозиторной архитектурой. Большинство таких систем предназначены для обработки транзакций по отношению к базам данных. Предположим, например, что электрическая компания имеет базу данных. В ней хранятся данные о клиентах: месячное потребление энергии, баланс, история платежей, ремонты и т. д. Типичными операциями для этой базы данных являются добавление нового клиента, операция кредитования, запрос на историю платежей. Типичный проект для репозиторных архитектур такого рода представлен на рис. 5.28.
Другие примеры репозиторных архитектур можно найти в области интерактивных сред разработки (IDE — Interactive Develompent Environment). Интерактивные среды разработки широко применяют такие процессы, как редактирование и компиляция в базу данных исходных кодов и объектных файлов.
Существует масса литературы, посвященной архитектурам баз данных, и в этой книге мы не будем пытаться охватить весь материал по данной теме. Многие приложения, к примеру IDE, не затрагивают базы данных, пока последние не закончены. Проект игры Встреча в его простейшей форме не содержит базы данных. Однако если игра вырастет до размеров нескольких десятков персонажей, то может оказаться предпочтительней хранить персонажи в базе данных, а не в специальных файлах. И уж точно нам не обойтись без базы данных, если мы разрешим пользователю запрашивать статистику, например «количество персо-
Рис. 5.28. Типичная репозиторная архитектура
Архитектуры досок объявлений, разработанные для приложений с искусственным интеллектом, являются репозиториями с определенными правилами поведения. Детальное рассмотрение архитектур досок объявлений приведено в [66].
И последней из репозиторных архитектур, которые мы рассмотрим, будет гипертекстовая архитектура. Использование этой архитектуры широко распространено в Web. Другим примером могут служить приложения, управляющие документацией по разработке программного обеспечения (см. рис. 1.19).
Слово «репозиторий» зачастую используется для указания того, что приложение предоставляет унифицированный интерфейс доступа к коллекции баз данных. Репозитории не изменяют структуры баз данных, а только предоставляют унифицированный доступ к ним. Этот особый случай репозиторных архитектур определен Гарланом и Шоу в [34].
Репозиторные архитектуры используются в огромном количестве приложений, поскольку базы данных, которые нужно поддерживать, присутствуют во многих архитектурах. Когда обработка незначительно отличается от форматирования данных из базы данных, репозиторные архитектуры являются самыми подходящими. С другой стороны, наличие большой базы данных иногда свидетельствует о том, что значительный объем обработки данных может влиять на архитектуру. В приложениях, которые изначально не следовало проектировать с помощью репозиторной модели, быстро распространяется программирование ad hoc
(например, с использованием хранимых процедур).
5.3.4.1. «Визит» к членам репозитория образца проектирования Iterator.
Репозиторные архитектуры находят применение в приложениях, где необходима агрегация (коллекция) объектов. Агрегация представлена в таком виде, чтобы ее элементы можно было «посетить». В случае, когда агрегированные объекты могут «посещаться» несколькими способами, целесообразно использовать образец проектирования Iterator. Примером может служить система управления персоналом, требующая выдавать список персонала различными способами. Предположим, что объекты служащих хранятся в виде дерева, согласно организационной диаграмме. Клиентское программное обеспечение для множества служащих может быть реализовано так, что посещение структуры данных будет осуществляться способом, определенным параметром Iterator. Этот параметр выполняет операции над каждым индивидуальным объектом, такие как распечатка имен, запись всего досье служащего в файл. Посещение может происходить различными способами: в алфавитном порядке, по должности и т. д.
нажей с силой более 10». Для выражения подобных запросов используется язык SQL (Structured Query Language — язык структурированных запросов) [72].
Предположим, что agg — это агрегация объектов класса С. Объекты Iterator, посещающие agg, должны иметь следующие четыре функции.
♦ Iterator «указывает» на первый элемент: void setToFirstO;.
♦ true, если Iterator «указывает» на последний элемент: boolean isDoneO;.
♦ Превращает Iterator в указатель на следующий элемент: void increment О;.
♦ Возвращает элемент, на который указывает Iterator: С getcurrentElementO;.
После того как объекты Iterator определили эти четыре функции, мы можем создавать использующий их код. Таким образом, мы выполняем операции агрегации во время исполнения в том порядке, в каком они определены в объекте Iterator:.
/*.
Выполняем желаемую операцию над элементами агрегации в соответствии с номером итерации (i): */.
for (i .setToFirstO : i.isDoneO; i. increments)) операция(i.getcurrentElement());.
Мы могли бы не использовать Iterator, если бы посещения происходили только одним способом.
5.3.5. Уровневые архитектуры.
Уровень архитектуры — это логически связанная коллекция артефактов программного обеспечения, обычно — пакеты классов. В общем виде уровень использует не более одного уровня и вместе с тем используется не более чем одним уровнем. Построение приложений последовательно уровень за уровнем сильно упрощает процесс. Некоторые уровни, например каркасы, могут использоваться в нескольких приложениях.
Мы уже видели, как уровиевый подход применяется к приложению Встреча. В этом случае классы в пакетах наследуются от классов в каркасных пакетах. Еще один пример — использование ускорителя трехмерной графики в качестве уровня, доступного с уровня ролевой игры Встреча — показан на рис. 5.29.
Рис. 5.29. Уровневые архитектуры
Пример многоуровневой архитектуры для банковского приложения печати Ajax представлен на рис. 5.30. В последнем случае имеется четыре уровня архитектуры. Зависимости между уровнями архитектуры показаны на рис. 5.31 в порядке, обратном используемому на рис. 5.30. Уровень приложения Ajax Bank Printing должен отвечать за печать и форматирование. Он построен на основе уровней Accounts и Ajax Bank Common Library. Последние построены на основе уровня, поддержку которого осуществляет продавец (на рис. 5.30 не показан). Этот уровень содержит общие утилиты, такие как средства сортировки и поиска. Обычно уровень реализуется в виде пакета классов. Например, Ajax Bank Common Library включает в себя классы, используемые в Ajax-приложениях. В качестве отношений может использоваться наследование, агрегация или объектная ссылка. Например, между уровнями допустима только агрегация.
Клиент-серверная архитектура является общей уровневой формой. В этой форме уровень клиента связывается с серверным уровнем для получения требуемого сервиса. Клиент обычно находится резидентно на компьютере пользователя, а сервер — на более крупном централизованном компьютере. Сервер нередко ссылается на базу данных.
Классическая клиент-серверная архитектура обычно страдает от реализации. Дело в том, что зачастую клиенты и серверы закодированы на уровне технических средств в высокой степени зависимости друг от друга. Эта проблема может быть решена с помощью трехуровневой архитектуры, в которой средний уровень предназначен для разделения клиента и сервера. Средний уровень можно использовать для увеличения гибкости архитектуры несколькими способами. Например, если несколько серверов одновременно могут обслужить запрос клиента, то средний уровень может динамически определить подходящий сервер. Средний уровень обычно реализуется в виде связующего программного обеспечения (middleware). Стандартом такого программного обеспечения является архитектура CORBA, разработанная консорциумом OMG.
Рис. 5.30. Пример уровневой архитектуры с использованием агрегации
Рис. 5.31. Возможное использование уровневой архитектуры в игре Встреча
Уровневые архитектуры имеют множество преимуществ при повторном использовании. Библиотека классов Java является очень эффективной уровневой системой (пакет appl et — уровень — основан на пакете awt, который в свою очередь основан на пакете lang, и т. д.).
5.3.6.
Приложения со смешанной архитектурой.
Приложения обычно используют несколько архитектур. Например, каркас для ролевых видеоигр может использовать несколько типов архитектур, приведенных Гарланом и Шоу (см. рис. 5.31). Это будет иметь смысл, например, если организовать пакет Артефакты как базу данных. Персонажи игры могут рассматриваться как параллельные взаимодействующие процессы; систему управления игрой мы можем организовать как систему обработки событий.
5.3.7.
Подведение итогов: процедура выбора архитектуры.
Подведем итоги нашему обсуждению выбора и разработки архитектуры.
ОДИН ИЗ СПОСОБОВ ВЫБОРА АРХИТЕКТУРЫ (1) -.
1.
Разбейте систему на замкнутые модули.
2.
Сравните со стандартными архитектурами (из классификации Гарлана и Шоу). Улучшите декомпозицию.
♦ Есть поток данных в пакетах между обрабатывающими станциями? Архитектура последовательных пакетов.
♦ Обрабатывающие станции ожидают получения входных данных, чтобы начать свою работу?.
Архитектура каналов и фильтров. + Процессы выполняются параллельно?.
Архитектура параллельных взаимодействующих процессов.
♦ Процесс обеспечивает обслуживание пользовательских процессов? Клиент-серверная архитектура.
♦ Процесс реагирует только на происходящие события? Системы, управляемые событиями.
♦ Приложение состоит из процессов, которые выполняются по сценарию? Образец проектирования Interpreter.
+ Приложение строится для хранилища данных? Репозиторные архитектуры.
♦ Существует упорядочение по уровням? Уровневые архитектуры.
ОДИН ИЗ СПОСОБОВ ВЫБОРА АРХИТЕКТУРЫ (2) -.
3.
Сделайте выбор среди представленных альтернативных архитектур.
4.
Добавьте к классам, полученным на основе анализа требований, классы, обеспечивающие согласование с выбранной архитектурой.
Например, в системе, управляемой событиями, это могут быть классы, контролирующие переходы между состояними.
5.
Примените существующий каркас и (или) образец проектирования, если найдете полезный.
6.
Распределите классы по пакетам.
В идеале должно быть 4-8 пакетов (для больших приложений используйте вложенные пакеты).
Каждый пакет должен иметь смысл в контексте приложения (например, пакет Видеофильмы допустим, а пакет БольшиеКлассы — нет).
7.
Удостоверьтесь, что связность между частями высока. Низкое сцепление будет подтверждением правильного выбора.
8.
Рассмотрите возможность добавления facade-Knacca (объекта) для управления интерфейсами пакетов.
5.4. Архитектура: нотация, стандарты и инструментальные средства.
5.4.1.
Нотация.
UML — это широко распространенная графическая нотация для изображения объектно-ориентированных проектов. В этой книге освещаются некоторые аспекты U ML.
Мы рассмотрели нотацию переходов состояний в разделе 3.3.4 и диаграммы потоков данных в разделе 5.3.1. Они могут применяться независимо от объектной ориентированности. Это верно и для диаграмм «объект—отношение», показывающих отношения между данными в хранилищах данных.
5.4.2.
Инструментальные средства.
Для облегчения процесса разработки программного обеспечения используется множество автоматизированных инструментальных средств. Некоторые из них представляют собой коллекцию классов с различными взаимосвязями. Примерами таких коллекций могут служить Rational Rose от Rational Corporation и Together от Object International. Эти инструменты облегчают построение объектных моделей, а также их соединение с соответствующим исходным кодом и диаграммами последовательности.
Для выбора инструментальных средств моделирования составляется список требований к ним. Этот процесс аналогичен процессу анализа требований для разработки программного приложения. Приведем список некоторых требований к инструментам моделирования.
♦ [необходимо] Облегчение изображения объектных моделей и диаграмм последовательности.
♦ Быстрое создание классов.
♦ Легкое редактирование классов.
♦ Изменение масштаба изображения внутри частей модели.
♦ [желательно] Возможность быстрого перехода от объектной модели к исходному коду.
♦ [необходимо] Должен стоить не более $Х для одного пользователя.
♦ [не обязательно] Возможность обратного проектирования (то есть создания объектной модели из исходного кода).
5.4.2.1. Высокоуровневые и низкоуровневые инструментальные средства.
Пакеты инструментальных средств зачастую пытаются охватить и архитектуру, и детальное проектирование, и реализацию. Различные продавцы разрабатывают системы с возможностью использования гиперссылок между исходным кодом и документацией. Инструментальные средства, ориентированные на реализацию, подобные Javadoc, могут быть хорошим дополнением для процесса разработки. Клиент-серверные инструменты, такие как Powerbuilder, вполне пригодны для определения архитектуры, хотя они определяют и реализации. Javadoc очень полезен при навигации по пакетам, поскольку он предоставляет алфавитный список всех классов, а также их иерархию.
Интерактивные среды разработки (IDE) укомплектованы компиляторами и используются как инструменты частичного моделирования. Объектно-ориентированные IDE в основном показывают наследование в иерархической форме. Этот факт привлекает разработчиков из-за близости этих инструментов к процессам компиляции и отладки. Однако IDE обычно имеют недостаточно широкий спектр возможностей, чтобы облегчить построение архитектуры и проектировочную работу.
Инструментальные средства сборки компонентов позволяют создавать приложения с помощью перетаскивания значков, представляющих элементы процессов. Среды JavaBeans представляют собой пример таких инструментальных средств. В таких средах объекты Java, классы которых соответствуют стандарту Java Beans, можно взять из библиотек или создать самостоятельно и связать через события. Стандарт Java Beans был создан для облегчения таких простых сборок с помощью графических инструментальных средств.
Основное неудобство при использовании инструментов моделирования связано с зависимостью проекта от третьей стороны — продавца. Вдобавок ко всему, кроме сложности самого приложения и проекта разработчик должен беспокоиться о жизнеспособности продавца. Если продавец обанкротится или обновление для инструментальных средств станет слишком дорогим — как это отразится на проекте?.
Но несмотря на все это популярность инструментов моделирования растет во всем мире. Продолжительность их использования ограничивается продуктивностью и экономическими факторами.
5.4.3. Стандарт IEEE/ANSI для описания проекта.
Стандарт IEEE 1016-1987 (вновь утвержденный в 1993 году) для проектной документации программного обеспечения (SDD — Software Design Document) содержит руководство по составлению и ведению документации по разработке. Оглавление этого документа представлено далее. Руководство, включенное в стандарт IEEE 1016.1-1993, объясняет, каким образом SDD может быть составлена для различных стилей архитектур. Большинство из них мы уже рассмотрели. Упражнения в конце главы используют стандарт IEEE с некоторыми модификациями для подчеркивания выразительности объектно-ориентированной парадигмы. Разделы стандарта 1-5 могут быть отнесены к архитектуре программного обеспечения, а раздел 6 — к детальному проектированию.
1.
Введение.
1.1.
Цель.
1.2.
Описание проекта.
1.3.
Определения, сокращения и термины.
2.
Ссылки.
3.
Описание декомпозиции.
3.1.
Модульная декомпозиция.
3.1.1.
Описание модуля 1.
3.1.2.
Описание модуля 2.
3.2.
Декомпозиция на параллельные процессы.
3.2.1.
Описание процесса 1.
3.2.2.
Описание процесса 2.
3.3.
Декомпозиция данных.
3.3.1.
Описание блока данных 1.
3.3.2.
Описание блока данных 2.
4.
Описание зависимостей.
4.1.
Межмодульные зависимости.
4.2.
Межпроцессные зависимости.
4.3.
Зависимости внутри данных.
5.
Описание интерфейса.
5.1. Модульный интерфейс.
5.1.1.
Описание модуля 1.
5.1.2.
Описание модуля 2.
5.2. Интерфейс процессов.
5.2.1.
Описание процесса 1.
5.2.2.
Описание процесса 2 6. Детальное проектирование.
6.1.
Детальное проектирование модулей.
6.1.1.
Модуль 1: детали.
6.1.2.
Модуль 2: детали.
6.2.
Детальное проектирование данных.
6.2.1.
Блок данных 1: детали.
6.2.2.
Блок данных 2: детали.
5.5. Контроль качества при выборе архитектуры.
Персонал, осуществляющий контроль качества, должен принимать участие в оценках архитектуры. Кроме того, он разрабатывает планы тестирования для всех компонентов архитектуры, начиная с того момента, как эти компоненты определены.
5.5.1. Качество и выбор архитектуры 5.5.1.1. Метрики для выбора архитектуры.
Большинство приложений могут быть реализованы с помощью различных архитектур. Некоторые варианты могут быть намного лучше других. Такие важные решения, как выбор архитектуры, не принимаются без первичной разработки и сравнения альтернатив. Предложенные архитектуры тщательно анализируются, поскольку устранение дефектов на ранней стадии стоит намного меньше, чем их исправление во время реализации проекта.
В этом разделе мы предложим метрики для выбора архитектуры, а в следующем рассмотрим примеры выбора наиболее подходящей архитектуры.
Один из способов выбора — присвоить веса требуемым характеристикам и назначить нечеткий коэффициент качества для каждого кандидата (табл. 5.3). Таблица 5.3 может быть использована для сравнения вариантов во многих областях при условии, что критерии могут быть выбраны и взвешены. Следующие метрики из [59] применяются к проектированию программного обеспечения почти повсеместно.
«13. Количество входов и выходов для модуля (пакета). Эта величина может быть вычислена путем учета количества доступных извне модуля открытых методов. Количество выходных точек вычисляется путем подсчета открытых функций, которые возвращают значения вызвавшему их объекту или производят изменения в среде вне этого модуля (пакета). Основной целью является минимизация этой величины для уменьшения количества взаимодействий».
«15. Графико-теоретическая сложность архитектуры. Простейшей (статической) версией этой метрики является выражение:.
Количество модулей в архитектуре - Количество модулей, имеющих хотя бы один вызов функции между ними +1».
Таблица 5.3. Нечеткий метод сравнения архитектур
Характеристика
Архитектура 1
Архитектура 2
Архитектура 3
Качество (Вес: 1-10) 9 = Высокое; 5 = Среднее; 2 = Низкое
Расширение (е)
еа1
ea2
еаЗ
Изменение (с)
са1
ca2
саЗ
Простота (s)
sal
sa2
sa3
Эффективность: скорость(esp)
espal
espa2
espa3
Эффективность: хранение (est)
estal
esta2
esta3
Итого:
e*ea1+c*ca1+s*sa1 + +esp*espa1+est*esta1
e*ea2+c*ca2+s*sa2+ +esp*espa2+est*esta2
e*ea3+c*ca3+s*sa3+ +esp*espa3+est*esta3
Применим метрику 15 к архитектуре системы, имитирующей работу банка (рис. 5.32). Архитектура делит имитацию на следующие пакеты:.
+ SimConfiguration — определяет способ распределения средств между станциями внутри банка;.
4- Simltems — определяет объекты, которые перемещаются внутри банка;.
♦ SiшЕvents — обрабатывает настоящие и будущие события, имеющие место в банке (то есть прибытие клиента к окну обслуживания);.
♦ Simulation — механизм, управляющий имитацией (выбирает следующее событие для исполнения, исполняет его, обрабатывает последовательности, включая генерацию результирующих событий, и составляет очередь из них в объекте SheduledEvents);.
♦ Random — пакет, осуществляющий генерацию чисел на основе различных распределений (например, генерирует длительность обслуживания следующей транзакции).
Эта архитектура разработана с помощью образца проектирования Facade. Для расчета метрики будем учитывать ссылки только между пакетами. В архитектуре имеется пять узлов (пакетов), а также пять пар модулей, между которыми имеются вызовы функций (в любую сторону). Таким образом, сложность статической архитектуры 5-5+1 = 1. Это число также характеризует количество циклов внутри архитектуры (его можно вычислить непосредственно как 1 - SimDriver/Events/Configuration). В результате мы выясняем, что данная архитектура не является сложной, что, в общем-то, хорошо.
Рис. 5.32. Архитектура системы, имитирующей работу банка
«25. Сложность потока данных или информации. Этот показатель измеряет поток информации в крупномасштабных объектах, сложность потоков в процедурах и модулях, а также сложность соединений между модулями. Детальное определение находится в метрике 4.25 [59]».
Метрики наподобие этой предусматривают квантификацию. Но каким же образом использовать получившиеся числа? Один ответ мы можем найти в истории. Например, мы можем легко сказать, что пакет ИграВстреча на данный момент имеет четыре открытых функции (см. упражнения в конце главы). Возможно, мы можем предсказать, что количество этих функций в дальнейшем возрастет до величины от 10 до 15. Эти числа сравниваются с соответствующими средними значениями предыдущих проектов. Если среднее число 10 и мы удовлетворены декомпозицией предыдущих проектов, то полученная нами величина не вызывает никаких опасений. Если же среднее число 8, а мы получили 15, то следует внимательнее рассмотреть выбранную архитектуру.
5.5.2. Выбор из альтернативных архитектур.
Мы не будем подробно разрабатывать одну архитектуру при выборе из подходящих вариантов архитектур. В качестве примера рассмотрим архитектуру приложения Встреча.
♦ Вариант 1 для приложения Встреча — образец проектирования State. Он является возможной архитектурой для нашего приложения (мы обсуждали это в разделе 5.3.2.3). Мы будем сравнивать эту архитектуру с другим кандидатом. В качестве альтернативы возьмем архитектуру, рассмотренную в разделе 5.3.2.3 как вариант 1.
♦ Вариант 2 для приложения Встреча — специальная архитектура под управлением графического интерфейса пользователя (GUI). Вторую архитектуру можно совместить с идеей переходов состояний, а затем писать код обработки событий отдельно для каждого объекта GUI, который чувствителен к событиям мыши и клавиатуры. Такая архитектура показана на рис. 5.33. Для улучшения понимания на рисунке отображены выбранные методы. Выходные гиперссылки для этой архитектуры — это GUI представления объектов СоединениеЗоны, а каждому соединительному звену сопоставлен код обработки событий. Например, при выборе мышью выхода в зону подвал на экране должно появиться изображение этой зоны. Класс гиперссылок на зону подвал должен быть связан с соответствующим обработчиком события. Результат проектирования окажется более управляемым со стороны графического интерфейса пользователя, более специфичным и более зависящим от языка. Существует некоторое разделение между кодом для этого проекта и для компонентов, зависящих от состояния. С другой стороны, классовая модель содержит меньше классов.
Рис. 5.33. Специальная архитектура под управлением графического интерфейса пользователя для игры Встреча
♦ Вариант 3 для приложения Встреча — таблица переходов состояний. Третий вариант архитектуры основан на идее переходов состояний, но переход состояний будет реализован в виде таблицы. Такой вариант рассматривали Шлаер и Меллор в [99]. Примером управляющей таблицы является табл. 5.4. Эта архитектура использует концепцию переходов состояний, но реализует ее без образца проектирования State.
Таблица 5.4. Обработка событий перехода между состояниями, управляемых таблицей
Текущее состояние
Событие
Щелчок мышью для выхода
Запрос.
на изменение.
характеристики
Закрытие окна установки характеристик
Входит.
внешний.
персонаж
Уходит.
внешний.
персонаж
Ожидание
Отображение персонажа игрока в соседней зоне
Показать окно.
установки.
характеристик
Показать оба персонажа и перейти в состояние Контакт
Контакт
Вычислить результаты контакта и перейти в состояние Ожидание
Установка характеристик
Убрать.
установки окно характеристик и перейти в состояние Ожидание
Убрать окно установки характеристик и перейти в состояние Контакт
Перейти в состояние Ожидание
Теперь мы можем вычислить значения метрик, рассмотренных ранее для этой архитектуры. Существует список аргументов «за» и «против» для сравнения этих двух подходов. Более полное сравнение трех архитектур приведено в табл 5.5.
«За» использование образца проектирования State:.
♦ дает возможность легко добавлять или изменять состояния в соответстви с изменениями в проекте игры;.
♦ поясняет, какие действия должны быть выполнены в различных обстоятельствах;.
+ классифицирует все события мыши, имеющие какое-либо воздействие на игру Встреча.
«Против» использования образца проектирования State:.
♦ классовая модель более запутанна и изначально сложна для понимания.
♦ дублирование данных: состояние игры Встреча может быть получено из переменных, не относящихся к объекту State, что увеличивает вероятность ошибки программиста в случае противоречия.
«За» использование таблицы для описания переходов состояний:.
♦ таблица легко понимается и редактируется;.
♦ архитектура не обязательно должна быть реализована на объектно-ориентированном языке;.
♦ возможно документирование этого подхода с использованием метода Шлаера—Меллора [99].
«Против» использования таблицы для описания переходов состояний:.
♦ требует глобальную структуру данных (таблица);.
♦ пополнение таблицы новыми состояниями может нарушить существующий код и проект.
Сравнение трех архитектур при помощи метода сравнения из табл. 5.3 иллюстрирует табл. 5.5. Для свойств расширяемости и изменяемости были выбраны наибольшие веса, и поэтому архитектура с использованием образца проектирования вышла на первое место. Несмотря на использованные метрики, опытные команды стараются рассмотреть подробнее каждую архитектуру независимо.
Таблица 5.5. Пример использования нечеткого метода сравнения архитектур
Характеристика
Варианты архитектуры
1. Образец.
проектирования.
State
2. Специальный.
управляемый.
графическим.
интерфейсом.
пользователя
3. Таблица переходов состояний
Качество
Вес 1-10 Высокий = 9; Средний = 5; Низкий = 2
Расширение
9
Высокий
Низкий
Средний
Изменение
7
Высокий
Низкий
Средний
Простота
5
Низкий
Высокий
Средний
Эффективность: скорость
5
Средний
Высокий
Средний
Эффективность: хранение
2
Низкий
Низкий
Средний
Итого (чем больше, тем лучше):
183
126
140
5.5.3. Проверка архитектуры с помощью вариантов использования.
Варианты использования получают из требований заказчика. Поэтому варианты использования не могут учитывать архитектуру приложения по простой причине — она еще не определена. После выбора архитектуры полезно вернуться к рассмотрению вариантов использования и проверить, адекватно ли их поддерживает архитектура. Например, вариант использования «Войти в контакт с внешним персонажем» (см. рис. 3.6) должен исполняться в архитектуре, которую мы разработали в этой главе (см. рис. 5.10). Поскольку на протяжении всего проекта мы сохраняем классы предметной области, классы, на которые мы ссылались в вариантах использования, должны быть представлены среди используемых. Обычно диаграммы последовательности для вариантов использования на этом этапе включают дополнительные классы архитектуры.
5.5.4. Инспектирование выбора архитектуры.
Архитектуры проверяются на соответствие требованиям. Напомним, что выигрыш во времени и средствах максимален, если дефект обнаружен и устранен на начальной стадии проекта, например на уровне выбора архитектуры. Метрики, рассмотренные ранее, составляют один из базисов инспектирования архитектуры.
Инспектирование пакетов каркаса архитектуры для игры Встреча может привести к выводу, что еще нет никаких требований для появления пакета Артефакты и что этот пакет и есть собственно дефект.
В качестве дополнительного примера возьмем диаграмму переходов состояний, изображенную на рис. 5.34. Внимательное рассмотрение диаграммы переходов состояний может выявить дефекты, показанные на рисунке. Эти дефекты можно устранить путем уточнения имен и (или) определений событий, на которые выполняются ссылки.
Рис. 5.34. Дефекты в диаграмме переходов состояний для игры Встреча
5.5.5. Влияние выбора архитектуры на SPMP.
Теперь, когда мы определились с архитектурой, план-график можно сделать более определенным и детализированным. Также может быть определен порядок построения различных частей проекта. Например, имеет смысл разработать каркасный пакет Персонажи, прежде чем создавать пакет приложения ПерсонажиВстречи. Поскольку эти пакеты не будут закончены на первой итерации спирали, мы обозначим соответствующие задачи как Персонажи I и ПерсонажиВстречи I. Стрелками обозначим зависимость одного пакета от другого. Например, ПерсонажиВстречи I невозможно завершить, пока не закончена задача Персонажи I (рис. 5.35). План-график показывает, что Интеграция и Тестирование I не могут начаться до тех пор, пока не будут завершены все остальные задачи первой итерации.
Рис. 5.35. План-график проекта, модифицированный после выбора архитектуры
5.6. Подведение итогов.
Термин архитектура программного обеспечения имеет отношение ко всему процессу проектирования приложения на самом высоком уровне. Ниже представлена классификация архитектур программного обеспечения по Гарлану и Шоу [34]. ♦ Архитектуры потоков данных.
♦ последовательные пакеты;.
♦ каналы и фильтры.
4- Независимые компоненты:.
♦ параллельные взаимодействующие процессы;.
♦ клиент-серверные системы;.
♦ системы, управляемые событиями.
♦ Виртуальные машины:.
♦ интерпретаторы;.
♦ системы, основанные на правилах.
♦ Репозиторные архитектуры:.
+ базы данных;.
♦ гипертекстовые системы;.
♦ доски объявлений.
4- Уровневые архитектуры.
Каркас — это коллекция классов, которая применяется к семейству приложений. Образцами проектирования называются многократно используемые комбинации классов, решающие задачи, которые возникают достаточно часто. Некоторые из образцов проектирования могут применяться для построения архитектуры. Стандарт IEEE, который применяется к проектированию, называется SDD (Software Design Document). Выбор архитектуры является важнейшим решением. Он осуществляется между несколькими альтернативными архитектурами, что в общем виде можно выразить гак.
4- Каркас — это базис семейства приложений.
4- Образцы проектирования — это повторно используемые комбинации классов, решающие часто возникающие проектные задачи.
4- Стандарт IEEE SDD является полезной отправной точкой.
4- Создавайте и сравнивайте различные варианты архитектур.
В литературе вы можете встретить множество определений термина архитектура программного обеспечения. Некоторые из них приведены в [6].
Руководство по учебному проекту. Архитектура проекта Встреча.
В данном разделе мы рассмотрим принципы и методы, используемые при выборе архитектуры, а также способы представления архитектуры, о которых до сих пор рассказывалось в этой главе. Мы также поговорим о том, как архитектура реализуется на практике. В качестве примера мы будем рассматривать учебный проект видеоигры Встреча. Мы также рекомендуем студентам обращаться к врезкам «Один из способов...», которые во многом помогают в процессе обучения.
Этап 1. Подготовка.
В соответствии с SPMP, Карен Петере была лидером команды (а ее заместителем — Эд Браун) и вела проверку, контроль и наблюдение за работой по проектированию. Карен с самого начала хотела разработать две полновесных, детально проработанных альтернативных архитектуры для игры Встреча и вынести их обе на предварительное обсуждение командой разработчиков. Она решила предотвратить возможные столкновения между разработчиками и бесполезные выяснения того, кто разработал архитектуру лучше. Карен полагает, что в подобных случаях верх берет эго, а не техническая целесообразность. На ее памяти остались довольно неприятные случаи компромиссов, на которые пришлось пойти, чтобы «примирить» соревнующиеся архитектуры. В результате этого проект получался слабым, и работать с ним приходилось ежедневно месяцами, если не годами. С другой стороны, Карен не хотела создавать обособленную, изолированную архитектуру. Она решила, что вместе с Эдом они выберут кандидатуры архитектур, подробно и углубленно их изучат, а затем представят команде разработчиков типы архитектур на выбор.
Этап 2. Выбор архитектуры.
Эл Пруит, поразмышляв о проектировании приложения игры, представил на рассмотрение Карен набросок — приблизительную схему архитектуры, основанной на графическом пользовательском интерфейсе (см. рис. 5.33). Автор отмечает, что данная архитектура будет простой и легкой для реализации.
Эд и Карен заново просмотрели труды Гарлана и Шоу по классификации архитектур, с тем чтобы определить, не подходит ли игре Встреча какая-нибудь из названных там архитектур.
Сначала они задались вопросом, можно ли игру Встреча определить как поток данных от одного элемента обработки к другому. Данными в этом случае были бы положения персонажей и (или) значения их качеств. Однако такой подход не соответствовал их пониманию игры.
После этого, по Гарлану и Шоу, они обратились к архитектурам независимых компонентов. Первой из архитектур была архитектура параллельных коммуиика ционпых процессов. Карен полагает, что данная архитектура подходит игре: каждый персонаж можно рассматривать как отдельный процесс. Каждый из этих процессов может осуществляться в отдельном потоке, параллельном остальным, и эти потоки могли бы вступать между собой во взаимодействие каждый раз, когда персонажи встречаются друг с другом. Эта архитектура стала кандидатурой для игры.
Затем была рассмотрена схема «клиент-сервер». Однако в нашем случае не совсем ясно, кто будет выполнять роль клиента, и кто — роль сервера. Данная архитектура была признана неприемлемой.
Следующим типом архитектуры в списке были системы, управляемые событиями. Эта архитектура была признана подходящей, так как игра отвечает либо на события, инициируемые пользователем, такие как щелчок на гиперссылке зоны с целью войти в эту зону, либо на появление внешнего персонажа в той зоне, в которой находится персонаж игрока.
Далее были рассмотрены виртуальные машины. Основным критерием в данном случае стал вопрос: существует ли при выполнении приложения игры реальная необходимость в интерпретации сценария. В данном случае такой необходимости нет.
Затем рассматривалась возможнсть построения игры на основе репозитория данных (репозиторная система). Данные могут быть следующие: значения, относящиеся к персонажам, и значения, относящиеся к статусу игры. Было решено, что эта архитектура вполне применима в случае, если в игре много персонажей и артефактов, поскольку в этом случае преобладает манипуляция большими объемами информации. Поскольку игра Встреча не строится вокруг данных, предложение по использованию такой архитектуры было отклонено.
И наконец, была рассмотрена уровиевая архитектура. Здесь вопрос заключался в следующем: можно ли игру Встреча рассматривать как набор групп классов. При этом одна группа использовала бы одну или две другие группы. Карен полагает, что можно сформировать по крайней мере два полезных уровня: один — для ролевых игр в самом обобщенном смысле, другой — для самой игры Встреча. Данную архитектуру взяли на заметку. На этом перечень типов архитектур у Гарлана и Шоу закончился.
Получился следующий список возможных архитектур:.
♦ Архитектура, основанная на графическом пользовательском интерфейсе;.
♦ Параллельные коммуникационные процессы;.
♦ Системы, управляемые событиями;.
♦ Уровневая архитектура.
Далее Эд и Карен рассмотрели, какая из перечисленных моделей более всего соответствует архитектуре игры в целом, а какие могли бы выполнять вспомогательные функции. Они пришли к заключению, что уровни — это основной архитектурный принцип, поскольку в игре присутствует общий уровень ролевых игр и уровень игры Встреча. Было признано, что событийно-управляемые системы могли бы стать вспомогательной архитектурой по отношению к уровневой. Подробное обсуждение архитектуры параллельных коммуникационных процессов было отложено до этапа разработки персонажей игры. Все архитектурные замыслы отражены на рис. 5.9 в концептуальном ключе.
Архитектура событийно-управляемых систем реализуется посредством состояний и переходов между ними. Далее обсуждался вопрос, какие средства выбрать для описания событий и переходов: образец проектирования State или таблицу типа «состояние—действие». Было решено, что в выборе архитектуры смогут помочь метрики.
При помощи электронной почты они попытались согласовать с программистами выбор архитектуры, обсудив удельный вес критериев выбора (расширение, изменение и т. п., см. табл. 5.5). Было решено узнать мнение программистов относительно критериев до проведения самого собрания. При этом не указывалось, какие архитектуры были выбраны в качестве кандидатур для проекта. После этого, до проведения собрания, они отослали Элу Пруиту сравнительную таблицу, чтобы удостовериться, что при описании архитектуры не были упущены никакие критерии.
Эл отметил, что выбор архитектуры коренным образом зависит от удельного веса критериев, но сам он на стороне решения и взвешивания, которые будут предложены группой программистов. Карен и Эд составили электронную таблицу (см. табл. 5.4), сделав сравнительный анализ архитектуры, выдвинутой Элом Пруитом, и двух других архитектур, которые они разработали сами. Сравнительную таблицу разослали участникам группы, чтобы они ознакомились со сравнением и подготовились к собранию.
Этап 3. Собрание команды (предварительное обсуждение проектирования).
На собрании Карен и Эд первым делом утвердили решение, согласно которому сначала необходимо определить удельный вес критериев выбора архитектуры. Так как перед собранием все участники получили по электронной почте сообщение, они были в курсе дела. Однако о выборе самого типа архитектуры еще не говорилось.
Команде были предложены архитектуры на выбор; были показаны результаты сравнительного анализа в виде электронной таблицы. После обсуждения каждый из участников внес коррективы в свои оценки, и вся команда подтвердила свое согласие взять за основу уровневую архитектуру, а также использовать образец проектирования State. На собрании прошло детальное обсуждение выбранной архитектуры.
Были высказаны предложения по усовершенствованию архитектуры. Однако Карен решила не учитывать и не ранжировать эти предложения, чтобы не создавать на каждом собрании новую версию архитектуры. Предложения необходимо было обдумать.
Этап 4. Усовершенствование архитектуры.
Теперь перед Карен и Эдом стояла задача выполнить декомпозицию каждого из уровней. Для решения этой задачи они поместили два дополнительных архитектурных элемента в отдельные пакеты. На уровне ролевой игры они сформировали пакет для машины состояний под названием РолеваяИгра. Для управления персонажами игры, которые перемещаются параллельно, они создали пакет Персонажи. Они также создали пакет ИгроваяСреда, в котором содержатся классы с описанием зон, по которым будут перемещаться персонажи. И наконец, они предусмотрели возможность создания в перспективе пакета Артефакты для описания различных предметов, таких как боевые щиты, мечи, которые будут задействованы в игре. Для этого пакета, отложенного до будущих выпусков, предполагается репозиторная архитектура.
Декомпозиция уровня игры Встреча произошла аналогичным же образом, так как многие классы этого уровня наследуются от классов общего уровня игры. Было решено создать ограниченный доступ к пакетам данного уровня, чтобы предотвратить ситуацию, когда любой класс может ссылаться на любой другой.
Ясно, что в скором времени в процессе разработки и сопровождения станет трудно управлять этими ничем не ограниченными ссылками. Чтобы реализовать ограничение доступа к пакетам приложения, решено было использовать образец проектирования Facade. Эд возражал против такого шага по той причине, что это увеличивает объем кода, который необходимо писать программистам и с которым в дальнейшем необходимо будет иметь дело. «Методы будут вызываться не напрямую, — отметил он, — а только через специальные методы объектов Facade, в результате чего возрастает количество методов». В этом случае возникала еще одна проблема: внешние по отношению к пакету объекты не смогут ссылаться даже на внутренние классы пакета (но могут ссылаться на их общие базовые классы). Но Карен убедила Эда, что эту цену можно заплатить за достижение более четкого и ясного интерфейса в каждом пакете.
На следующем собрании команды эти решения по архитектуре были одобрены участниками.
Этап 5. Документирование архитектуры.
Чтобы предоставить описание архитектуры в терминах SDD, Карен и Эд использовали разделы 1-5 стандарта IEEE 1016. Поскольку приложение разделено на два уровня, один из которых предназначаен для дальнейшего использования в новых ситуациях, то они пришли к решению документировать каркасный уровень ролевой игры отдельным SDD.
Упражнения.
Ответы и подсказки для упражнений, помеченных символами «о» или «п», приводятся в конце этой главы.
Вопросы для проверки.
П5.1°. В одном-двух предложениях сформулируйте отношения между архитектурой и проектированием.
П5.2". В одном-двух предложениях сформулируйте отношения между архитектурой и каркасом.
П5.3. В классификации Гарлана и Шоу существуют пять категорий архитектур. Назовите по меньшей мере три из этих категорий.
П5.4
. Некоторые образцы проектирования затрагивают уровень архитектуры. Назовите три-четыре таких модели.
Общие упражнения.
05.Г. Студенты часто не замечают разницы между диаграммами потоков данных и диаграммами переходов состояний. Предположим, ваше приложение является симулятором, имитирующим работу с клиентами банка в пакетном режиме (Batch simulation). Сначала устанавливаются характеристики симулятора, затем симулятор выполняется без прерывания. Как можно описать это в терминах приложения, основанного на потоках данных? Для создания диаграммы используйте простую заготовку, состоящую из четырех частей. (Определите эти четыре части. А теперь подумайте, как можно представить данное приложение в виде диаграммы переходов состояний. Какая перспектива описания архитектуры представляется более выгодной?).
05.2. Какие из образцов проектирования — State, Observer или Intepreter — наилучшим образом подходят для следующих приложений:.
♦ приложение, информирующее клиентов о неожиданных изменениях на фондовом рынке;.
♦ приложение, позволяющее пользователям простым способом задавать стандартные типы писем при создании письма;.
♦ приложение реального времени, которое выдает сведения о техническом состоянии автомобиля.
Упражнения в команде.
К5.1. (Архитектура.).
Разработайте архитектуру для своего проекта. Опишите эту архитектуру в терминах стандарта IEEE. За образец можно взять пример, приведенный в данной главе. Ясно укажите, какой тип архитектуры и какие образцы проектирования вы применяете. Покажите по крайней мере еще один вариант архитектуры, который вам кажется подходящим для этого случая. Объясните, почему, по вашему мнению, описанный тип архитектуры может быть рассмотрен как альтернативный. Используйте метрики. Не требуется, чтобы вы выбрали архитектуру автоматически, опираясь только на метрики.
Определите время, потраченное на выполнение данного упражнения. За единицу отсчета времени мы предлагаем выбрать пять минут. Включите в проект электронную таблицу, в которой указывалось бы время, потраченное каждым участником проекта и командой в целом.
Возьмите за образец форму (табл. 5.6), в которой ведется запись времени, потраченного каждым из участников проекта на каждый модуль в отдельности. Сформулируйте свою точку зрения: окупилось ли время, потраченное вами на отслеживание временных затрат, или можно было обойтись без отслеживания.
Таблица 5.6. Форма для учета времени, потраченного на каждый модуль
Участник команды
Модуль
1
2
3
4
Смит
10
4
Джонс
5
12
Браун
2
14
Критерии оценки.
♦ Качество архитектуры: «отлично» — архитектура подобрана разумно и имеет разветвленную структуру модулей.
♦ Качество улучшения собственных навыков: «отлично» — полезная критика, написанная четким языком.
Ответы.
П5.1. Архитектура — это часть проекта, а имено элемент высшего«уровня. П5.2. Каркас — это архитектура, которую можно многократно использовать; в каркасе также могут содержаться проект и компоненты реализации. П5.4. Facade, State, Observer и Interpreter.
Подсказки.
05.1. Диаграмма потоков данных может состоять из следующих элементов обработки данных: выполнитьСледующееСобытие, обновитьПозициюКлиентов. Соответствующими хранилищами данных могут быть НамеченныеСобытия, где будут храниться все события, запланированные на будущее выполнение, и СтатусКлиентов, в котором хранится информация о том, какой статус имеет каждый из клиентов. Эти элементы завершают диаграмму потоков данных.
В данной архитектуре могут быть следующие состояния: Конфигурирование, Воспроизведение, ОповещениеОРезультатах.
Примеры.
В этом разделе мы приведем описание двух проектов. Первый — это проект каркаса ролевой видеоигры; второй — проект ролевой игры Встреча.
Документы SDD для обоих проектов разделены на две части. Первая часть, включающая разделы SDD с 1 по 5, приводятся ниже, и состоят из архитектурных аспектов проектирования. Вторая часть, раздел 6, которая обсуждается в конце главы 6, представляет собой детальное проектирование.
Зависимость игры Встреча от каркаса рассматривается в примере игры Встреча.
Пример 1. Каркас архитектуры ролевой игры.
[Примечание для студентов. Содержание данного раздела приводится в разделе 5.4.3.] История версий данного документа, x/yy/zzz К. Петере: первоначальный черновой эскиз.
x/yy/zzz К. Петере: усовершенствованная и сильно измененная схема пакета, x/yy/zzz Э. Браун: критический обзор, замечания.
x/yy/zzz К. Петере: пересмотрена работа с компонентами, включенными в нее Р. Боствиком.
x/yy/zzz К. Петере: детали классов перемещены в раздел 3.
1.
Введение.
1.1.
Цель.
В данном документе приведено описание пакетов и классов каркаса ролевой видеоигры.
1.2.
Описание проекта.
Каркас отражает сущность и основы классов ролевой игры. Он создается с учебной целью, чтобы продемонстрировать пример каркаса. Он не предназначен для создания каркаса для коммерческих игр. Он невелик по размерам, что способствует обучению.
1.3.
Определения, сокращения и термины.
Каркас — собрание взаимосвязанных классов, которые используют для создания семейств приложений посредством наследования или агрегации.
РИ (ролевая игра) — видеоигра, в которой персонажи взаимодействуют. Характер взаимодействия зависит от характеристик персонажей и от среды, в которой они находятся.
2.
Ссылки.
Software Engineering: an Objected-Oriented Perspective. E. Braude, Wiley, 2000.
UML: The Unified Modeling Language User Guide. G. Booch, J. Rumbaugh, I. Jacobson, Addison-Wesley, 1998 [15].
Стандарт IEEE 1016-1987 (утвержденный заново в 1993 году) устанавливает основные направления разработки SDD.
3.
Описание декомпозиции.
[Примечание для студентов. В данном разделе указывается точно, как следует группировать классы каркаса ролевой видеоигры. Это предполагает декомпозицию на верхнем уровне: детальная декомпозиция, в частности на методы, откладывается до этапа детального проектирования (см. пример в конце главы 6).].
3.1. Модульная декомпозиция.
[Примечание для студентов. В данном разделе демонстрируется, как происходит декомпозиция. Разъяснение по каждому этапу приводится в соответствующем подразделе.].
Каркас состоит из пакетов РолеваяИгра, Персонажи, Артефакты и Схема. Их декомпозиция показана на рис. 5.36. Классы этих пакетов поясняются далее.
Все классы являются открытыми, если не указано иное. По пометкам на языке UML (выделены на рис. 5.36 курсивом) можно сделать вывод, что все классы каркаса являются абстрактными.
Рис. 5.36. Каркас ролевой видеоигры
3.1.1.
Пакет РолеваяИгра.
Данный пакет спроектирован как машина переходов по состояниям. Основная идея заключается в том, что ролевая игра всегда находится в одном из нескольких состояний. Данный пакет позволяет описать возможные состояния игры и те действия, которые могут происходить в ответ на события. В пакете реализуется образец проектирования State [Ga]. Состояние игры инкапсулировано в определенный объект СостояпиеИгры, а он, в свою очередь, агрегируется одиночным объектом РолеваяИгра. Этот агрегированный объект называется состояние. Другими словами, состояние — это атрибут объекта РолеваяИгра, относящийся к типу СостояпиеИгры.
Функция handleEventO класса РолеваяИгра вызывается, чтобы управлять каждым событием, возникающим на мониторе (щелчком мыши и т. п.). Она выполняется при вызове функции состояния handleEventO. Применяемая версия функции handleEventO зависит от подкласса СостояпиеИгры, которому принадлежит свойство состояние.
3.1.2.
Пакет Персонажи.
[Примечание для студентов. Может показаться странным наличие пакета, содержащего всего один класс. Однако в области проектирования программного обеспечения все имеет тенденцию расти. Даже если класс не растет, это еще не говорит о его ненужности. Пример пакета всего с одним классом — java.applet, это пакет с одиночным классом Applet (но в нем содержится также несколько интерфейсов).].
Этот пакет содержит класс ПерсонажИгры, в котором дается описание персонажей игры.
3.1.3.
Пакет ИгроваяСреда.
В данном пакете дается описание физической среды, в которой проходит игра. Класс СхемаИгры агрегирует объекты соединений. Каждый объект соединения агрегирует пару объектов ИгроваяЗона, которые он связывает. Данная архитектура позволяет осуществлять многочисленные соединения между двумя зонами.
Каждый объект ИгроваяЗона агрегирует персонажи игры, которые он содержит (если содержит), и может фиксировать встречу персонажей.
3.1.4.
Пакет Артефакты.
[Не реализован — для будущих выпусков.].
Данный пакет предназначен для хранения элементов, которые следует расположить в зонах, таких как деревья или столы, а также предметов, которыми владеют персонажи, например щитов и ранцев.
3.2. Декомпозиция на параллельные процессы.
Каркас не включает в себя и не затрагивает параллельные процессы.
4.
Описание зависимостей.
[Примечание для студентов. В данном разделе приводится описание всех зависимостей между модулями.].
Единственная зависимость между модулями каркаса заключается в агрегации класса ПерсонажИгры классом ИгроваяЗона.
5.
Описание интерфейса.
Все классы в данных пакетах являются открытыми, таким образом, интерфейсы складываются из всех методов их классов.
Пример 2. Архитектура ролевой игры Встреча. SDD, часть 1.
Часть 2 приводится в детальном проектировании примера.
[Примечание для студентов. Содержание данного раздела приводится на рис. 4.2.].
История версий этого документа, x/yy/zzz К. Петере: первоначальный эскиз, x/yy/zzz К. Петере: составлена общая схема, x/yy/zzz Э. Браун: выявление дефектов.
x/yy/zzz К. Петере: дополнительные компоненты, предложенные Э. Брауном, x/yy/zzz К. Петере: дополнительная разбивка на компоненты согласно вариантам использования и модели переходов состояний.
1.
Введение.
1.1.
Цель.
В данном документе приведено проектирование ролевой видеоигры Встреча.
1.2.
Описание проекта.
Этот проект представляет собой прототип видеоигры Встреча, создаваемый с учебной целью, на котором мы продемонстрируем приемы разработки архитектуры, детального проектирования и составления документации. Предполагается использовать выбранную архитектуру в качестве основы для будущих более совершенных версий. В это описание не входят каркасные классы — их разработка документируется в разделе SDD под названием «Каркас архитектуры ролевой игры».
1.3.
Определения, сокращения и термины.
Нет.
2.
Ссылки.
Software Engineering: an Objected-Oriented Perspective. E. Braude, Wiley, 2000.
UML: The Unified Modeling Language User Guide. G. Booch, J. Rumbaugh, I. Jacobson, Addison-Wesley, 1998 [15].
Стандарт IEEE 1016-1987 (утвержденный заново в 1993 году) устанавливает основные направления разработки SDD.
3.
Описание декомпозиции.
Для описания архитектуры Встречи используются три модели: вариантов использования, классов и переходов состояний. Кроме того, будут показаны также отношения между каркасными пакетами Встречи, описанные в разделе SDD «Каркас архитектуры ролевой игры».
[Примечание для студентов. Мы дополняем стандарт IEEE, вводя в этот документ разделы 3.4. и 3.5. Напомним, что как альтернативу можно рассматривать модель, основанную на потоках данных, однако в нашем случае она не кажется нам особенно полезной. В данном случае для этой видеоигры мы придерживаемся модели переходов состояний как при составлении требований, так и при проектировании.].
3.1. Модульная декомпозиция.
[Примечание для студентов. В этом разделе не должно повторяться описание детального проектирования, о котором пойдет речь в следующей главе. Здесь мы не должны углубляться в подробности далее содержимого пакетов.].
Пакеты архитектуры Встречи показаны на рис. 5.37. Эти три пакета: ИграВстреча, ПерсонажиВстречи и СредаВстречи. Им соответствуют faeade-классы ИграВстреча, РолиВстречи и СредаВстречи. Каждый из этих классов имеет ровно
Рис. 5.37. Архитектура и модули ролевой видеоигры Встреча
3.1.1.
Пакет ИграВстреча.
Пакет ИграВстреча состоит из классов, управляющих развитием игры в целом. Этот пакет разрабатывается для того, чтобы обеспечить реакцию на действия пользователя (события).
3.1.2.
Пакет ПерсонажиВстречи.
Пакет ПерсонажиВстречи заключает в себе персонажи, участвующие в игре. Это и персонажи, находящиеся под управленим игрока, и внешние персонажи.
3.1.3.
Пакет СредаВстречи.
Пакет СредаВстречи описывает схему игры Встреча, а именно зоны и соединения между ними. К этой категории не относятся какие-либо движущиеся предметы.
3.2.
Декомпозиция на параллельные процессы.
В игре Встреча имеется два параллельных процесса. Первый включает в себя основное действие игры, в котором игрок перемещает свой главный персонаж из одной зоны в другую. Второй процесс состоит из перемещений внешнего персонажа между зонами.
3.3.
Декомпозиция данных.
[Примечание для студентов. В этом разделе приводится описание структур данных, используемых в приложении.].
Структуры данных, которыми обмениваются пакеты, определяются в классах Зона, ПерсонажВстречи и СоединениеЗопыВстречи.
3.4.
Декомпозиция модели переходов состояний.
Состояния видеоигры Встреча показаны на рис. 5.38.
IПримечание для студентов. Эта диаграмма переходов состояний соответствует диаграмме, используемой в SRS при описании требований. Прочие состояния, упоминаемые в требованиях, будут реализованы в последующих версиях.]
один экземпляр и по сути является интерфейсом для взаимодействия с пакетом. Остальные классы недоступны за пределами пакета (описание образца проектирования Facade см. в разделе 5.3.2.1 и [33]).
Рис. 5.38. Диаграмма переходов состояний видеоигры Встреча
3.5. Декомпозиция модели вариантов использования.
[Примечание для студентов. Этот подраздел мы добавили к IEEE-спецификации, которая не приветствует варианты использования. Поскольку он добавлен в конец раздела, то он никак не нарушает стандартный порядок.].
В видеоигре Встреча можно выделить три варианта использования: «Инициализировать», «Перейти в соседнюю зону» и «Вступить в контакт с внешним персонажем» (рис. 5.39). Эти варианты использования подробно описываются в разделе 2.2 SRS, а также в последующих разделах данного документа.
Рис. 5.39. Варианты использования видеоигры Встреча
4. Описание зависимостей.
В этом разделе дается описание зависимостей между различными вариантами декомпозиции, перечисленными в разделе 3 данного документа.
[Примечание для студентов. Между вариантами использования нет каких-либо значительных зависимостей.].
4.1. Межмодульные зависимости (объектная модель).
Зависимости между пакетными интерфейсами иллюстрирует рис. 5.40. Пакет ИграВстреча является зависимым по отношению ко всем пакетам Встречи.
Рис. 5.40. Архитектура видеоигры Встреча
Пакет СредаВстречи находится в зависимости от пакета ПерсонажиВстречи. Это происходит потому, что любое взаимодействие персонажа игры возможно только в контексте среды. В частности, объекты класса Зона отвечают за выявление одновременного присутствия персонажа игрока вместе с внешним персонажем в одной и той же зоне.
Связи между классами, не относящимися к интерфейсу, объясняются далее в этом документе.
4.2.
Межпроцессные зависимости.
В том случае, когда происходит контакт, во взаимодействие вступают два процесса: процесс перемещения основного персонажа игрока и процесс управления перемещением внешнего персонажа.
4.3.
Зависимости внутри данных.
Структуры данных, которыми обмениваются пакеты, определены в классах. Взаимодействие классов описано в разделе 6 данного документа.
4.4.
Зависимости между состояниями.
Каждое состояние зависит от тех состояний, в которые игра может перейти из него.
4.5.
Зависимости между уровнями.
Зависимость приложения Встреча от каркаса ролевой игры показана на рис. 5.41. Каждым пакетом приложения используется именно один каркасный пакет.
Рис. 5.41. Зависимости между пакетами приложения и каркаса
5. Описание интерфейса.
В этом разделе описываются интерфейсы объектной модели. Обратите внимание, что некоторые из описываемых классов определены при описании проектирования каркаса ролевой игры.
5.1. Межмодульные интерфейсы.
[Примечание для студентов. В этом подразделе описывается взаимодействие между пакетами.].
5.1.1.
Интерфейс пакета ИграВстреча.
Интерфейс пакета ИграВстреча обеспечивается объектом играВстреча класса ИграВстреча. Перечислим его состав.
1.
EncounterGame getTheEncounterGameO // получение единственного экземпляра.
2.
GameState getStateQ // текущее состояние экземпляра ИграВстреча.
3.
void setState О // устанавливает состояние экземпляра ИграВстреча.
4.
// Любое событие, оказывающее влияние на экземпляр ИграВстреча: void hand!еЕvent ( AWTEvent ).
5.1.2.
Интерфейс пакета ПерсонажиВстречи.
Интерфейс пакета ПерсонажиВстречи обеспечивает объект действующиеЛицаВстречи класса РолиВстречи. Перечислим его состав.
1.
EncounterCast getTheEncounterCastO //получение одиночного экземпляра.
2.
GameCharacter getThePlayerCharacterO //уникальный персонаж игрока.
3.
GameCharacter getTheForeignCharacterO //уникальный внешний персонаж.
4.
// Обмен значениями характеристик в зависимости от зоны, в которой происходит контакт:.
void engageP1ayerWithForeignCharacter( GameArea ) 5.1.3. Интерфейс пакета СредаВстречи.
Интерфейс пакета СредаВстречи обеспечивается объектом средаВстречи класса СредаВстречи. Перечислим его состав.
1.
EncounterEnvironment getTheEncounterEnvironmentO //получение объекта.
2.
GameArea getArea( String ).
3.
GameAreaConnection getAreaConnection( String ).
4.
void moveForeignChatacterTo( Area ) throws AreaNotAdjacentException.
5.
Image getNeighborhoodAreas( Area ).
// получение зоны Area и зон, удаленных на одно или два соединения.
5.2. Интерфейс процессов.
(Примечание для студентов. Мы утверждаем в разделе 3.2, что в игре Встреча имеется два процесса. Это ваэ/сное для проектирования решение существенно затрагивает интерфейс процесса управления внешним персонажем. Поясним это. Одним из варинатов может быть внешний персонаж в виде потока, который будет управлять собой сам. Такой подход обладает некоторыми преимуществами, но требует, чтобы внешний персонаж либо получал сведения о среде (это может помешать дальнейшему расширению игры), либо был в состоянии определять среду динамически (что было бы изящным решением, но слишком амбициозно для учебного проекта). Архитектура располагает к другому варианту, который и описывается далее.].
5.2.1.
Процесс перемещения персонажа игрока.
Интерфейс процесса, управляющего перемещениями персонажа игрока, состоит из графического интерфейса пользователя, который описан в SRS. Этот процесс обеспечивает реакцию на события, описанные в разделе 3.4, которые управляются пакетом ИграВстреча в соответствии с их спецификацией (см. далее в этом документе).
5.2.2.
Процесс перемещения внешнего персонажа.
Процесс перемещения внешнего персонажа представляет собой отдельный процесс, находящийся под управлением объекта ИграВстреча, с которым он связан. Этот процесс управляется методами, наследуемыми от java.lang.Thread.

Популярные статьи

Свежие статьи