Подсистемы должны определяться таким образом, чтобы большая часть взаимодействий оставалась внутри подсистем, для уменьшения глобальных потоков данных и сокращения зависимостей между подсистемами. Подсистем должно быть не очень много (в пределах десятка). Некоторые подсистемы могут быть в свою очередь подразделены на подсистемы.
Две подсистемы могут взаимодействовать друг с другом либо как клиент и поставщик (клиент-сервер), либо как равноправные партнеры (сопрограммы). В первом случае клиент вызывает сервер, который выполняет некоторый запрос клиента и возвращает результат; клиент должен знать интерфейс сервера, но сервер может не знать интерфейсов клиента, так как все взаимодействия инициируются клиентом. В случае программного взаимодействия обе подсистемы вызывают друг друга. Обращение подсистемы к другой подсистеме не обязательно связано с немедленным получением ответа. Программное взаимодействие является более сложным, так как обе подсистемы должны знать интерфейсы друг друга. Поэтому нужно стараться, чтобы большая часть подсистем взаимодействовала как клиент и сервер.
Подсистемы (и реализующие их модули) могут образовывать в системе уровни, либо разделы.
Уровневая система может рассматриваться как упорядоченное множество виртуальных миров, каждый из которых построен на основе понятий, поддерживаемых его подсистемами; подсистемы одного уровня обеспечивают реализацию подсистем следующего уровня. Объекты каждого уровня могут быть независимыми, хотя нередко объекты разных уровней могут соответствовать друг другу. Каждая подсистема знает о подсистемах более низких уровней и ничего не знает о более высоких уровнях. Зависимость клиент-сервер существует между верхними (клиентами) и более нижними уровнями (серверы). При этом каждый уровень может иметь свой собственный набор классов и операций. Каждый уровень реализуется через операции объектов и подсистем, более нижних уровней. Уровневые архитектуры бывают двух видов: открытые и замкнутые. В замкнутой архитектуре каждый уровень строится на базе непосредственно следующего за ним уровня. Это сокращает зависимости между уровнями и упрощает внесение изменений. В открытой архитектуре каждый уровень строится на базе всех следующих за ним уровней. Это уменьшает потребность в переопределении операций на каждом уровне и приводит к более эффективному и компактному коду. Однако открытая архитектура не удовлетворяет принципу инкапсуляции информации, поскольку изменения в какой-либо подсистеме могут потребовать соответствующих изменений в подсистемах более высоких уровней.
Обычно лишь подсистемы самого верхнего и самого нижнего уровней могут быть выведены из постановки задачи: самый верхний уровень - это требуемая система, а самый нижний уровень - это доступные ресурсы: аппаратура, операционная система, имеющиеся библиотеки. Промежуточные уровни вводятся разработчиком системы.
Система с уровневой архитектурой при переносе на другую платформу требует переписывания только одного (самого нижнего) уровня. Пример системы с уровневой архитектурой представлен на рисунке 2.1.
Рисунок 2.1 - Пример системы с уровневой архитектурой
Разделы подразделяют систему на несколько независимых или слабо связанных модулей (подсистем), каждая из которых обеспечивает один из видов услуг. Например, операционная система компьютера включает систему файлов, управление процессами, управление виртуальной памятью и управление устройствами.
Обычно система подразделяется на модули (подсистемы) с использованием обоих способов разбиения в самых разных комбинациях: уровни делятся на разделы, разделы содержат в себе уровни.
Рисунок 2.2 - Топология звезды
Когда все модули и подсистемы всех уровней названы, необходимо показать информационные потоки между модулями и подсистемами, построив ДПД. Это позволит понять топологию системы. Топология системы определяется совокупностью потоков информации в системе; например, у компилятора конвейерная топология, можно представить себе топологию звезды (рисунок 2.2) и т.п.; нужно стремиться, чтобы топология системы была как можно проще.
2.1.2. Выявление асинхронного параллелизма
В анализируемой модели, как и в реальном мире и в программном обеспечении все объекты независимы и должны работать асинхронно. Однако в реализации не все объекты должны работать независимо и асинхронно, так как несколько объектов может выполняться на одном процессоре, если известно, что они не могут быть одновременно активными. Одна из важнейших целей разработки системы выяснить, какие объекты должны быть активными одновременно (параллельно), а какие бывают активными только в разное время. Эти последние объекты могут быть связаны в одну нить управления (задачу).
Для определения асинхронности (параллельного существования) объектов используется динамическая модель. Два объекта являются существенно асинхронными, если они могут получать события в одно и то же время, не взаимодействуя друг с другом. Если события не синхронизируются, такие объекты не могут быть связаны в одну нить управления.
От асинхронных объектов нужно отличать полностью независимые объекты, которые не только выполняются независимо, но и не обмениваются данными в процессе своей работы. Полностью независимые системы удобны тем, что их можно поместить на разные аппаратные устройства, исключив при этом коммуникационные затраты.
Асинхронные объекты могут выполняться и на одном устройстве (процессоре), если оно имеет систему прерываний и поддерживает режим разделения времени. Во многих случаях необходимость выполнять объекты на разных устройствах следует из постановки задачи.
2.1.3. Распределение модулей и подсистем по процессорам и задачам
Каждый асинхронный (независимый) объект (модуль или подсистема) должен быть приписан к одному из устройств аппаратуры: универсальному процессору или специализированному функциональному устройству. Разработчик системы должен:
оценить требуемую производительность и требуемые ресурсы;
выбрать способ реализации подсистем (аппаратный или программный);
распределить программные подсистемы по процессорам, стремясь удовлетворить их требования по производительности и в то же время сократить межпроцессорные коммуникации.
Решение использовать несколько процессоров обычно бывает связано с потребностью иметь более высокую производительность, чем производительность одного процессора. Количество требуемых процессоров зависит от объема вычислений и производительности компьютера. Разработчик системы может оценить требуемую производительность процессоров, вычисляя постоянную нагрузку как произведение количества транзакций в секунду на время выполнения одной транзакции (для банковской сети транзакция - это проводка). Такая оценка не учитывает накладных расходов, связанных со случайными изменениями нагрузки и некоторыми другими факторами. Ее следует уточнить.
Аппаратуру следует рассматривать как неизменяемое тщательно оптимизированное программное обеспечение. Каждое устройство может рассматриваться как объект, который работает параллельно с другими объектами. Разработчик может принять решение о замене некоторых объектов подходящими аппаратными устройствами. Обычно такое решение принимается по следующим причинам:
требуемые устройства легко доступны; в наше время легче купить процессор с плавающей арифметикой, чем реализовать соответствующую библиотеку;
требуется более высокая производительность, чем производительность имеющихся процессоров, а производительность специализированных устройств, всегда выше.
При распределении модулей и подсистем по процессорам следует иметь в виду следующее:
некоторые задачи нужно выполнять на определенных устройствах; например, обработку банковской карточки следует выполнять на БМ;
время ответа или скорость информационного потока превышает пропускную способность канала между процессором и программой; например, высокоскоростные графические устройства требуют спаренных контроллеров;
скорости вычислений слишком высоки для одного процессора, и задачи нужно разместить на нескольких процессорах; подсистемы, которые часто взаимодействуют, нужно поместить на одном процессоре.
2.1.4. Управление хранилищами данных
Внутренние и внешние хранилища данных являются четкими точками раздела между подсистемами с хорошо определенными интерфейсами. В качестве хранилищ данных могут использоваться базы данных, файлы и другие структуры данных, размещенные во внешней или основной памяти. Выбор вида хранилища данных зависит от ситуации.
В базах данных обычно размещают данные, удовлетворяющие одному из следующих условий:
данные, для которых требуется доступ на высоком уровне детализации со стороны многих пользователей;
данные, которые могут эффективно управляться командами СУБД;
данные, которые должны переноситься на многие платформы;
данные, для которых требуется доступ со стороны нескольких прикладных программ.
В файлах удобно размещать данные, удовлетворяющие одному из следующих условий:
данные, которых много, но которые плохо поддаются структуризации;
данные с низкой информационной плотностью (например, дампы);