Глава 12
Управление: контроль за движением поездов
Программная индустрия развилась настолько, что охватывает многие новые области приложений: от встроенных микрокомпьютеров для управления двигателем автомобиля до выполнения рутинной работы при изготовлении фильмов и обеспечения интерактивного доступа миллионов телезрителей к базам видеоинформации. Отличительной особенностью таких больших систем является их чрезвычайная сложность. Конечно, построить компактную реализацию системы - задача почетная, но некоторые большие задачи несомненно требуют большого объема кода. В крупных проектах нередко участвуют программистские организации в сотни человек, которые должны написать миллионы строк кода. Программы должны удовлетворять требованиям, неизбежно меняющимся в процессе работы. Как правило, в рамках таких проектов создается не одна программа, работающая на одном компьютере, а комплекс программ, функционирующих в параллельной распределенной среде на нескольких компьютерах, связанных между собой разнообразными каналами передачи информации. Для того, чтобы уменьшить вероятность неудачи, в таких проектах предусматривается обычно центральная организация, отвечающая за архитектуру и целостность системы. Некоторые части системы нередко выполняются по субконтрактам другими компаниями. Таким образом, команда разработчиков никогда не собирается вместе, она распределена в пространстве и, - так как в больших проектах происходит постоянное обновление кадров, - во времени.
Если за создание большой системы возьмется разработчик, который занимался написанием в оконной среде небольших программ, рассчитанных на одного пользователя, его несомненно испугают возникающие проблемы; возможно, даже настолько, что он сочтет глупостью попытку создать такую программу. Но действительность такова, что большие системы должны строиться. И в некоторых случаях глупо не попытаться. Вообразим себе ручное управление авиационными полетами во круг столичного аэропорта, систему жизнеобеспечения космической станции, зависящую от "человеческого фактора" или ведение учета в международном банке, выполняемое на счетах. Успешная автоматизация таких систем приводит не только к решению очевидных проблем, но и приносит множество неожиданных выгод: снижение эксплуатационных расходов, повышение надежности, увеличение функциональных возможностей. Конечно же, ключевое слово здесь - успешная. Из всего сказанного понятно, что создание больших систем - чрезвычайно трудная задача. Поэтому при ее решении необходимо применять все лучшее из инженерной практики и использовать интуицию ведущих проектировщиков.
В этой главе представлена как раз такая задача. Она демонстрирует, как объектно-ориентированное проектирование облегчает выполнение сверхбольших программных проектов.
12.1. Анализ
Определение границ проблемной области
Для большинства люден, живущих в США, поезда являются символом давно ушедшей эпохи. В Европе и странах Востока ситуация совершенно противоположная. В отличие от США, в Европе мало национальных и международных автомобильных магистралей, а цены на бензин и газ сравнительно высоки. Поэтому поезда составляют основу транспортной сети континента; по десяткам тысяч километров путей ежедневно перевозится множество людей и грузов - и в отдельных городах, и между различными странами. Ради справедливости отметим, что в США поезда играют по-прежнему важную роль в перевозке грузов. С разрастанием городов их центры становятся все более и более перегруженными, и на легкий рельсовый транспорт возлагаются надежды решить проблему перегрузки и загрязнения окружающей среды двигателями внутреннего сгорания.
Железные дороги по-прежнему являются коммерческими и, следовательно, они должны быть прибыльными. Железнодорожные компании обязаны постоянно поддерживать баланс между требованиями экономии и безопасности и нарастающей интенсивностью перевозок с одной стороны и эффективным и предсказуемым расписанием - с другой. Эти противоречия наводят на мысль, что решения об управлении движением поездов необходимо принимать автоматически, и, в том числе, производить контроль за всеми элементами железной дороги с помощью компьютера.
Такие автоматические и полуавтоматические системы сегодня существуют в Швеции, Великобритании, Германии, Франции и Японии [1]. Подобная система, называемая Продвинутой Системой Управления Железнодорожным Транспортом, была разработана в Канаде и США с участием следующих компаний: Amtrak, Burlington, Canadian National Railway Company, CP Rail, CSX Transportation, Norfolk and Western Railway Company, Southern Railway Company, Union Pacific. Эффект от каждой из этих систем был и экономический, и социальный; результатом их внедрения стало снижение эксплуатационных затрат, повышение эффективности использования ресурсов, безопасность.
Требования к системе управления движением
Система управления движением выполняет две главные функции: выбор маршрутов железнодорожных перевозок и контроль систем, обеспечивающих перевозки. Эти функции включают: планирование перевозок, контроль местонахождения поездов, контроль за перевозками, предотвращение конфликтов, прогнозирование нарушении, регистрацию всех операций. На рис. 12-1 показана схема основных элементов системы управления движением [2].
Рис. 12-1. Система управления движением
Система анализа и отображения информации на локомотиве состоит из множества дискретных и аналоговых датчиков для контроля за такими параметрами, как температура, давление масла, количество топлива, напряжение и сила тока на генераторе, число оборотов вала двигателя в минуту, температура воды, тяговая мощность. Значения параметров с датчиков поступают к машинисту через дисплейную систему, а к диспетчеру и обслуживающему персоналу вне поезда - через сеть. Предупреждение или сигнал тревоги выдается и регистрируется всякий раз, когда показания датчика выходят за пределы нормального режима. Журнал показаний датчиков используется при проведении эксплуатационных работ и для управления расходом топлива.
Система управления энергией в режиме реального времени подсказывает инженеру поезда, как наиболее эффективно использовать установку. Входными данными для этой системы являются: профиль и качество пути, ограничения по скорости, расписание, загрузка поезда, максимальная развиваемая мощность. Исходя из этих данных, система может определить оптимальный по расходу топлива режим работы двигательных установок, согласующийся с заданным расписанием и требованиями безопасности. Рекомендации системы, профиль и качество пути, местоположение и скорость поезда могут отображаться с помощью бортовой системы индикации.
Бортовая система индикации обеспечивает человеко-машинный интерфейс для машиниста. На нее может выводиться информация из системы анализа и отображения информации на локомотиве, системы управления энергией и блока управления данными. Специальные клавиши позволяют машинисту просматривать различные данные.
Блок управления данными представляет собой шлюз между всеми бортовыми системами поезда и глобальной сетью передачи данных, к которой подключены все поезда, диспетчеры и прочие пользователи.
Отслеживание маршрутов движения поездов осуществляется с помощью подключенных к сети передачи данных ответчиков местоположения и глобальной спутниковой системы указания местоположения (GPS, Global Positioning System) Navstar. Система анализа и отображения информации на локомотиве может вычислять пройденный путь с помощью счетчика, подсчитывающего число оборотов колеса. Эта информация дополняется данными ответчиков местоположения, которые размещены через каждый километр пути или чаще (на важнейших развилках). Ответчики передают информацию о себе на проходящие поезда (используя блок управления данными), что позволяет более точно определить местоположение. Кроме того, поезд может быть оснащен приемниками GPS, с помощью которых его географическое положение может быть определено с точностью до метра.
Блок интерфейса путевых устройств размещается там, где есть какое-либо управляемое устройство (например, стрелка), или датчик (например, инфракрасный датчик для обнаружения перегрева подвесок колес). Каждый блок интерфейса получает команды (например, команды на включение и выключение сигнала) от локального наземного контроллера. Устройства могут быть переведены в ручной режим управления. Кроме того, каждое устройство может сообщать свои установочные параметры. Наземный контроллер транслирует информацию на блоки интерфейса путевых устройств и обратно, а также на проходящие мимо поезда и обратно. Контроллеры расположены вдоль железнодорожного пути через такие расстояния, чтобы любой поезд всегда находился в зоне действия хотя бы одного из них.
Каждый наземный контроллер передает свою информацию на объединенную систему управления сетью. Связь между системой управления сетью и наземным контроллером может осуществляться по радио в микроволновом диапазоне, по наземным линиям или по оптоволокну в зависимости от удаленности данного контроллера. Система управления сетью обеспечивает функционирование всей сети. Она может автоматически направлять информацию по другому маршруту в сети, если на одном из путей произойдет отказ оборудования,
Система управления сетью, в свою очередь, подсоединяется к одному или нескольким диспетчерским центрам, которые объединены в систему управления операциями. Система управления сетью соединена и с другими пользователями. В системе управления операциями диспетчеры могут задавать маршруты поездов и отслеживать их передвижение. Для управления различными участками выделяются отдельные диспетчеры; каждая диспетчерская управляющая консоль отвечает за одну или несколько территорий. Маршрутизация поездов подразумевает выдачу инструкций для автоматического перевода поезда с пути на путь, установку ограничения скорости, управление пропуском автомобилей на переездах, разрешение и запрещение движения поезда в зависимости от занятости определенных участков пути. Диспетчеры могут наблюдать за состоянием путей впереди по маршруту поезда и передавать эту информацию машинисту. Поезда могут быть остановлены системой управления операциями (вручную диспетчерами или автоматически), когда обнаруживается опасность (выход поезда из графика, повреждение пути, возможность столкновения). Диспетчеры могут также вызвать на экран любую информацию, доступную машинистам отдельных поездов, разослать распоряжения по движению, установить параметры путевых устройств и пересмотреть план движения.
Расположение путей и путевое оборудование могут со временем меняться. Число поездов и маршруты их движения могут изменяться ежедневно. Система должна обеспечивать возможность подключения новых датчиков, сетей и оборудования, выполненных по более совершенным технологиям.
На врезке сформулированы основные требования к системе управления движением поездов. Очевидно, они сильно упрощены. На практике детальные требования к большой системе вырабатываются после демонстрации жизнеспособности программного решения проблемы. При этом анализ отменяет сотни человеко-месяцев труда с участием экспертов в данной области и пользователей системы. В конечном счете требования к системе могут состоять из тысяч страниц документации, специфицирующей не только базовое поведение, но и такие детали, как макеты форм интерфейса.
Но даже исходя из наших упрощенных требований, мы можем сделать два замечания о разработке системы управления движением:
-
Архитектура должна быть открыта для развития.
-
Реализация должна опираться на существующие стандарты.
Наш опыт разработки больших систем показывает, что первоначальная формулировка требований никогда не бывает полной, она всегда в некоторой степени неопределенна и противоречива. Соответственно, мы должны быть готовы управлять возникающими в процессе разработки неопределенностями. Мы настоятельно рекомендуем осуществлять эволюцию подобных систем в виде пошагового, итеративного процесса. Как уже говорилось в главе 7, сам цикл разработки дает пользователям и разработчикам возможность понять, какие требования на самом деле существенны; именно процесс разработки, а не упражнения в чистописании спецификаций в отсутствии готовой частичной реализации или прототипа. Кроме того, необходимо учитывать, что на создание большой системы может быть затрачено несколько лет. За это время сильно изменится аппаратная часть [В действительности для многих систем такого уровня сложности характерно, что в них входят компьютеры самых разнообразных типов. Хорошо продуманная и стабильная архитектура смягчает риск смены техники в процессе разработки, которая сплошь и рядом происходит в быстро меняющемся компьютерном мире. Новые модели приходят и уходят, поэтому важно четко представлять границу между техникой и программами, чтобы можно было ввести в систему новые компьютеры или контроллеры, снижающие затраты или улучшающие характеристики работы, и сохранить при этом целостность архитектуры]. Поэтому требования к программе должны предусматривать адаптацию к новой технике. Бессмысленно создавать элегантную архитектуру для аппаратуры, которая гарантированно устареет за время разработки. Мы считаем, что в архитектуру программной системы следует включать только те аппаратные особенности, которые непосредственно опираются на существующие стандарты: связь, сети передачи данных, графику и протокол работы датчиков. Для совершенно новых систем иногда Приходится становиться первопроходцами аппаратных и программных средств. Это приводит к повышению риска, который для большинства систем и без того высок. Разработка программного обеспечения, особенно, когда речь идет об успешном завершении большого приложения, неизбежно связана с риском, и наша цель - снизить этот риск до минимума.
Очевидно, что мы не сможем подробно рассмотреть все вопросы анализа и проектирования описанной системы в одной главе или даже в одной книге. Так как наша задача - показать, как работают обозначения и методология, сосредоточимся на построении гибкой архитектуры изучаемой области.
Системные и программные требования: хрупкий компромисс
Крупные проекты, подобные рассматриваемому, обычно организуются вокруг небольшой центральной группы, ответственной за глобальную архитектуру системы, а сама разработка передается сторонним субподрядчикам или другим группам внутри той же организации. Уже на стадии анализа системные архитекторы имеют некоторую концептуальную модель, которая разделяет аппаратную и программную части реализации. Многие, правда, считают, что это уже не анализ, а проектирование. Это - спорный вопрос. В самом деле, трудно решить, что показано на схеме рис. 12-1- исходные требования или проект системы. Но в любом случае схема предполагает, что на данной стадии разработки архитектура системы принципиально объектно-ориентированна. Например, на схеме присутствуют такие сложные объекты, как система управления энергией или система управления операциями. Каждый из них выполняет одну из основных функций всей системы. Это как раз то, о чем говорилось в главе 4: объекты самого высокого уровня абстракции отвечают за основные функции системы. Поэтому процесс анализа в данном случае мало отличается от процесса проектирования.
Когда мы уже имеем скелет архитектуры (как на рис. 12-1), можно с помощью экспертов в данной прикладной области приступать к разработке основных сценариев поведения системы, как это было описано в главе 6. Чтобы подробнее описать ожидаемое поведение системы, можно использовать диаграммы взаимодействия, диаграммы объектов, протоколы действий или прототипы. На рис. 12-2 приведена диаграмма взаимодействия компонент системы, отражающая сценарий подготовки ежедневных приказов по движению поездов. На данном уровне анализа нас интересуют именно основные события и взаимодействия, определяющие поведение системы. Такие детали, как сигнатуры операций и ассоциации - это тактические подробности, которые понадобятся на последующих фазах проектирования.
В системе таких размеров запросто можно найти сотни первичных сценариев [Мы встречали проекты программных систем, в которых одни только результаты анализа занимали больше 8000 страниц документации - несомненный знак слишком ревностного анализа. Начинающийся с этого проект редко бывает удачным]. В главе 6 мы уже установили "правило 80%". Это значит, что до перехода к проектированию архитектуры желательно зафиксировать 80% важнейших сценариев. Дожидаться 100% готовности бессмысленно.
Рис. 12-2. Подготовка ежедневных приказов по движению.
Очевидно, нужно перевести требования к системе на язык требований к ее программной и аппаратной частям, чтобы различные компетентные организации могли одновременно заниматься отдельными частями задачи (но обязательно под присмотром некоторой центральной группы, обеспечивающей общее видение проекта). Совместное создание аппаратного и программного обеспечения - сложная задача, особенно, если эти части слабо связаны и создаются разными фирмами. Иногда ясно, какая аппаратура будет использоваться. Например, можно использовать готовые терминалы или рабочие станции для бортовых дисплейных систем и в центрах управления операциями. Аналогично, представляется вполне очевидным, что составлением расписаний поездов занимаются программы. Окончательное решение о том, какую основу, аппаратную или программную, использовать в каждом конкретном случае, зависит от предпочтений разработчиков не меньше, чем от всего остального. Специализированную аппаратуру можно использовать, когда важнее производительность, а использование программ целесообразнее, когда необходимо обеспечить гибкость.
Будем считать, что первоначальный вариант аппаратной архитектуры выбран архитекторами системы. Этот выбор не должен считаться окончательным, но по крайней мере он дает отправную точку для уточнения требований к программному обеспечению. В ходе анализа, а затем и проектирования, нам необходима свобода в выборе аппаратной или программной реализации той или иной функции: позднее может оказаться, что нужна дополнительная аппаратура, или что данную функцию можно реализовать программно.
Рис. 12-3. Диаграмма процессов системы управления движением.
На рис. 12-3 показано целевое аппаратное обеспечение для системы управления движением; здесь используются наши обозначения для диаграмм процессов. Эта архитектура процессов соответствует схеме на рис. 12-1. В частности, предусмотрен один бортовой компьютер на каждом поезде, соединяющий систему сбора и передачи информации о локомотиве, систему управления энергией, бортовой дисплей и устройство управления данными. Мы предполагаем, что некоторые бортовые устройства, такие, как дисплей, обладают минимальным интеллектом, но, возможно, не все они программируемые. Мы полагаем, что каждый ответчик подсоединен к передатчику, который посылает сообщения на проходящий мимо него поезд; компьютер к ответчику местоположения не подсоединен. Все группы путевых устройств (каждое из которых логически состоит из интерфейса и переключателя) управляются компьютером, который может взаимодействовать с проходящим поездом или с наземным контроллером через их передатчики и приемники. Каждый наземный контроллер присоединяется через глобальную сеть к диспетчерскому центру (который входит в систему управления операциями). Для обеспечения бесперебойного обслуживания мы решили разместить на каждом диспетчерском центре два компьютера: основной и резервный (второй включится в случае отказа основного компьютера). В свободное время резервный компьютер может использоваться для обслуживания других, низкоприоритетных пользователей.
На эксплуатационном уровне система управления движением может содержать сотни компьютеров: по одному на каждый поезд, по одному на каждый блок интерфейса путевых устройств и по два на каждый диспетчерский центр. На диаграмме процессов показаны только некоторые компьютеры, так как излишне показывать повторяющиеся компоненты конфигурации.
Как уже говорилось в главах 6 и 7, здравый смысл подсказывает, что при разработке большого проекта огромную роль играют разумность и ясность интерфейсов между ключевыми частями системы. Особенно это важно для интерфейса между программной и аппаратной частями системы. В начале работы над проектом интерфейс может быть определен не полностью, но он должен быть достаточно быстро формализован, чтобы различные части системы можно было разрабатывать, тестировать и интегрировать одновременно. Хорошо определенный интерфейс позволяет производить сборку системы без существенных переделок ее частей. Кроме того, мы не рассчитываем, что все разработчики, участвующие в проекте, будут одинаково сильны в программировании. Поэтому мы должны поручить спецификации ключевых абстракций и механизмов сильнейшим системным архитекторам
Ключевые абстракции и механизмы
В результате изучения требований к системе управления движением становится очевидно, что мы должны решить четыре основные подзадачи:
-
сеть
-
база данных
-
интерфейс "человек/компьютер"
-
управление аналоговыми устройствами в реальном времени.
Как мы пришли к выводу, что именно в этих подзадачах сконцентрирован основной риск разработки?
Систему связывает воедино распределенная сеть передачи данных. С помощью радио передаются сообщения: между ответчиками и поездами, между поездами и наземными контроллерами, между поездами и блоками интерфейсов путевых устройств, между наземными контроллерами и путевыми устройствами. Кроме того, сообщения должны передаваться между диспетчерскими центрами и отдельными наземными контроллерами. Надежная работа всей системы обеспечивается своевременным и надежным приемом и передачей сообщений.
Кроме того, система должна одновременно хранить информацию о местоположении и планируемых маршрутах множества поездов. Мы должны поддерживать постоянно обновляемую информацию и гарантировать ее целостность даже в случае попыток одновременно записать и считать информацию из разных мест сети. Следовательно, нам нужна распределенная база данных.
Проектирование человеко-машинного интерфейса ставит еще одну группу задач. Дело в том, что пользователями системы в основном являются машинисты и диспетчеры; но никто из них не обязан обладать профессиональными навыками работы с компьютером. Пользовательский интерфейс операционных систем, таких как UNIX или Windows, пригоден (по большей части) для специалиста-программиста, но считается слишком враждебным для конечных пользователей таких сред, как система управления движением. Следовательно, все формы взаимодействия должны быть спроектированы в расчете на эту особую группу пользователей.
Наконец, система управления движением должна взаимодействовать с разнообразными датчиками и исполнительными механизмами. Не останавливаясь здесь на природе этих устройств, отметим, что принципы управления ими не зависят от конкретного типа устройства и должны быть выбраны однотипными во всей системе.
Каждая из этих четырех подзадач включает целый ряд обособленных вопросов. Системные архитекторы должны найти ключевые абстракции и механизмы каждой задачи, и тогда мы сможем пригласить экспертов для решения каждой отдельной подзадачи независимо от других. Однако, ни анализ, ни проектирование не удастся завершить за один проход, - круг за кругом анализ будет обнаруживать новые архитектурные проблемы, решение которых потребует нового анализа. Таким образом, разработка будет неизбежно пошаговой и итеративной.
Из краткого проблемного анализа четырех главных подзадач мы видим, что существуют три высокоуровневые ключевые абстракции:
• Поезда |
Локомотивы и вагоны. |
• Пути |
Профиль пути, его качество и путевые устройства. |
• Планы |
Расписания, приказы, устранение накладок, назначение полномочии и подбор бригад. |
Каждый поезд характеризуется текущим положением на путях и может иметь только один активный план движения. Аналогично, в каждой точке пути может быть самое большое один поезд. Каждый план относится только к одному поезду, но ко многим точкам пути.
Мы можем выделить ключевой механизм для каждой из четырех (почти независимых) подзадач:
-
передача сообщений
-
планирование движения поездов
-
отображение информации
-
сбор данных от датчиков.
Эти четыре механизма составляют душу нашей системы. Они являются наиболее сложными и рискованными частями проекта. Важно, чтобы мы поручили лучшим системным архитекторам поэкспериментировать с различными подходами и постепенно создать среду, на базе которой более молодые разработчики сделают все остальное.
12.2. Проектирование
Как уже отмечалось в главе 6, создание архитектуры подразумевает выявление основной структуры классов и спецификацию общих взаимодействий, которые оживляют классы. Сконцентрировав внимание прежде всего на этих механизмах, мы с самого начала выявляем элементы наибольшего риска и нацеливаем на них все усилия системных архитекторов. Результаты этой фазы дают хорошую основу (в виде классов и взаимодействий), на базе которой строятся функциональные элементы нашей системы.
В данном разделе мы подробно рассмотрим семантику каждого из четырех выделенных ключевых механизмов.
Механизм передачи сообщений
Под сообщением здесь мы не имеем в виду активизацию методов, как это принято в объектно-ориентированных языках программирования. В данном случае понятие взято из словаря предметной области, из самого высокого уровня абстракции. Вот несколько примеров сообщений в системе управления движением: сигнал запуска путевому устройству, сообщение о прохождении поезда через определенный пункт пути, приказ диспетчера машинисту. Все эти виды сообщений могут передаваться внутри системы управления движением на двух уровнях:
-
между компьютерами и устройствами
-
между компьютерами.
Сейчас нас интересует второй уровень передачи сообщений. Так как система включает территориально распределенную сеть, мы должны учесть такие факторы, как помехи, отказы оборудования и секретность передачи информации.
Первый шаг при определении сообщений в системе - анализ взаимодействия каждой пары сообщающихся компьютеров (см. рис. 12-3). Для каждой такой пары мы должны задать три вопроса: (1) Какую информацию обрабатывает каждый компьютер? (2) Какая информация будет передаваться с одного компьютера на другой? (3) К какому уровню абстракции будет относиться эта информация? Эмпирического ответа на эти вопросы нет. Мы должны действовать итеративно, пока не придем к уверенности, что определены правильные сообщения и в системе связи нет "узких" мест (которые могут возникать из-за перегрузки линий связи или, например, из-за того, что сообщение разбивается на слишком мелкие пакеты).
Очень важно, чтобы на данном этапе проектирования внимание было сосредоточено на сути, а не на форме сообщений. Слишком часто системные архитекторы начинают проектирование с выбора битового представления сообщений. В реальной задаче преждевременный выбор низкоуровневого представления обязательно приведет к изменениям в дальнейшем и затронет всех, кто пользовался этим представлением. Кроме того, на ранней стадии проектирования у нас пока нет полной информации, как будут использоваться данные сообщения, и, следовательно, мы не можем судить, какое представление будет оптимальным по размеру и времени передачи.
Концентрируя внимание на сути сообщений, мы рассмотрим все классы сообщений. Другими словами, нужно определить назначение и смысл каждого сообщения, а также перечислить операции их обработки.
На диаграмме классов на рис. 12-4 показаны некоторые наиболее важные сообщения в системе управления движением. Заметим, что все сообщения в конечном счете являются экземплярами абстрактного класса Message, который инкапсулирует поведение, общее для всех сообщений. Три класса следующего уровня представляют главные категории сообщений: сообщение о состоянии поезда, сообщение о плане движения поезда, сообщение путевого устройства. Каждый из этих трех классов будет детализирован в дальнейшем. В результате проектирования должны появиться десятки специализированных классов. Таким образом, существование обобщающих абстрактных классов чрезвычайно важно; без них мы получили бы сотни несвязанных между собой и, следовательно, сложных в использовании модулей, каждый из которых реализовывал бы специализированный класс, По мере проектирования мы будем выявлять другие важные группы сообщений и создавать для них специализированные промежуточные классы. К счастью, изменения в иерархии классов не должны волновать клиентов, использующих классы.
Рис. 12-4. Диаграмма классов сообщений.
Прежде всего нам следует стабилизировать интерфейсы ключевых классов сообщений. Начинать этот процесс лучше всего с основных классов иерархии. Начнем с введения двух следующих типов;
//номер, обозначающий уникальный идентификатор пакета
typedef unsigned int PacketId;
//номер, обозначающий уникальный сетевой идентификатор
typedet unsigned Int NodeId;
Теперь дадим определение абстрактного класса Message:
class Message {
public:
Message();
Message(NodeId sender);
Message(const Message&);
virtual ~Message();
virtual Message& operator=(const Message&);
virtual Boolean operator==(const Message&);
Boolean operator!=(const Message&);
PacketId id() const;
Time timeStamp() const;
NodeId sender() const;
virtual Boolean isIntact() const = 0;
};
Этот класс отвечает за установку уникального идентификатора сообщения, отметки времени, идентификатора отправителя, целостность сообщения (а именно, класс проверяет, является ли оно синтаксически и семантически законным сообщением системы). Последнее поведение показывает, что сообщение - это нечто большее, чем просто запись данных. Как обычно сообщения должны еще обеспечивать операции копирования, переименования и проверки на равенство.
Когда наш проект будет содержать интерфейсы всех наиболее важных сообщений. мы сможем написать программы, основанные на этих классах, для моделирования создания и приема потоков сообщений. Такие программы можно использовать для тестирования различных частей системы.
Диаграмма классов на рис. 12-4, бесспорно, неполна. На практике в первую очередь необходимо разрабатывать наиболее важные сообщения, а все остальные добавлять по мере того, как будут обнаруживаться менее общие формы взаимодействия. Использование объектно-ориентированного проектирования позволит нам последовательно добавлять эти сообщения без нарушения существующих частей системы, так как возможность изменений учтена с самого начала.
Рис. 12-5. Передача сообщений.
Если мы удовлетворены структурой классов, то можно начать проектирование самого механизма передачи сообщений. Здесь возникают две конкурирующих между собой цели: придумать механизм, который обеспечит надежную доставку сообщении, но сделает это на достаточно высоком уровне абстракции, так, чтобы клиенту не надо было заботиться о способе доставки сообщения. Такой механизм передачи сообщений позволит клиентам ограничиться упрощенным представлением о процессе передачи.
На рис. 12-5 показан результат проектирования механизма передачи сообщений. Как видно на диаграмме, чтобы послать сообщение, клиент сначала создает новое сообщение м, затем передает его диспетчеру своего узла, который ставит сообщение в очередь для последующей отправки. Заметьте, что наш проект предусматривает для клиента возможность ожидания, если диспетчер узла не может осуществить отправку вовремя. Диспетчер получает сообщение как параметр и затем пользуется услугами объекта Transporter (передатчик), который обеспечивает необходимый формат сообщения и его рассылку по сети.
Мы сделали эту операцию асинхронной, чтобы клиент не ждал, пока сообщение будет отправлено по радио, что требует времени для кодирования, декодирования и повторных передач из-за помех. В конечном счете объект Listener (слушатель) принимает сообщение, преобразует его в принятую форму для диспетчера своего узла, который создает параллельное сообщение и ставит его в очередь. Получатель может заблокировать начало очереди сообщений, ожидая прихода следующего сообщения, которое передается как параметр синхронной операции nextMessage.
При проектировании диспетчера мы располагаем его на прикладном уровне сетевой модели ISO OSI [4]. Это позволит всем клиентам, передатчикам и приемникам работать на самом высоком уровне абстракции и общаться друг с другом в терминах, специфических для данного приложения.
Мы ожидаем, что окончательная реализация описанного механизма будет, вероятно, несколько более сложной. Например, может потребоваться шифровать и дешифровать сообщение и использовать коды обнаружения и исправления ошибок, чтобы обеспечить надежную связь в условиях помех на линиях связи и возможных отказов оборудования.
Планирование расписания поездов
Мы уже говорили, что концепция плана движения поезда является центральной для функционирования системы управления движением. Каждый поезд имеет один активный план, а каждый план предназначен только одному поезду. Он может содержать много различных приказов и точек на пути.
Наш первый шаг состоит в точном определении того, из каких частей состоит план поезда. Для этого мы должны перечислить всех потенциальных клиентов плана и выявить способ его использования каждым из них. Например, некоторым клиентам может быть разрешено составлять планы, другим - корректировать планы, а остальные смогут только читать планы. В этом смысле план выступает как хранилище информации, связанной с маршрутом одного отдельного поезда и действиями во время движения. Примером таких действий может быть отцепление или подцепление вагонов.
На рис. 12-6 приведены стратегические проектные решения, касающиеся структуры класса TrainPlan. Как и в главе 10, мы используем диаграмму классов, чтобы показать части, из которых состоит план движения поезда (подобно тому, как это делается на традиционных диаграммах "сущность-связь"). Мы видим, что каждый план содержит одну бригаду, но может включать в себя много приказов и действий. Мы ожидаем, что эти действия будут упорядочены во времени и что с каждым действием связана такая информация, как время, местоположение, скорость, ответственное лицо, приказы. Например, план может содержать следующие действия:
Время |
Положение |
Скорость |
Ответственное лицо |
Приказ |
0800 |
Pueblo |
Как указано |
Начальник депо |
Покинуть депо |
1100 |
Colorado Springs |
40 миль/ч |
|
Отцепить 30 вагонов |
1300 |
Denver |
40 миль/ч |
|
Отцепить 20 вагонов |
1600 |
Pueblo |
Как указано |
|
Вернуться в депо |
Из рис. 12-6 видно, что класс TrainPlan имеет один статический объект типа UniqueId, так называемое магическое число, однозначно идентифицирующее каждый экземпляр класса TrainPlan.
Как это делалось для класса Message и его подклассов, можно в первую очередь спроектировать наиболее важные элементы плана движения поезда; детали будут проясняться по мере того, как мы будем использовать план для разных клиентов.
Одновременное наличие огромного числа активных и неактивных планов поездов возвращает нас к проблеме базы данных, о которой мы уже говорили. Диаграмма классов на рис. 12-6 может служить наброском логической схемы этой базы данных. При этом возникает следующий вопрос: где хранится план поезда?
В совершенном мире, где нет помех или задержек при передаче и где неограниченны ресурсы компьютеров, лучше всего было бы разместить все планы движения поездов в единой центральной базе данных. Такой подход обеспечивает существование единственного экземпляра каждого плана. Однако реальные условия делают это решение неэффективным: неизбежны задержки при передаче, производительность процессоров ограничена. Таким образом, скорость доступа к плану, который расположен в диспетчерском центре, с поезда, не будет отвечать требованиям реального времени. Однако, с помощью программного обеспечения можно создать иллюзию централизованной базы данных. Наше решение заключается в том, что планы поездов будут располагаться на компьютерах диспетчерского центра, а копии этих планов будут по мере необходимости распределяться по узлам сети. Для обеспечения эффективности компьютер каждого поезда может хранить копию своего плана. Таким образом, бортовое программное обеспечение может получить нужные сведения с пренебрежимо малой задержкой. Если план изменяется в результате действий диспетчера или (что менее вероятно) по решению машиниста, наше программное обеспечение должно гарантировать, что все копии этого плана обновятся, причем за разумное время.
Рис. 12-6. Диаграмма классов TrainPlan (план движения
поезда).
На рис. 12-7 показано, как происходит передача и обновление копий плана. Первичная копия плана движения находится в централизованной базе данных в диспетчерском центре и может быть разослана по любому числу узлов сети. Когда какой-либо клиент (используя операцию get с уникальным UniqueId в качестве аргумента) запрашивает план, первичная версия копируется и посылается клиенту. В базе данных регистрируется местоположение копии, а сама копия плана сохраняет связь с базой данных. Теперь предположим, что в результате действий машиниста появилась необходимость изменить план движения поезда. Сначала изменяется копия плана, находящаяся на поезде. Затем сообщение об изменениях посылается в централизованную базу данных на диспетчерский центр. После того, как план изменился в базе данных, сообщения об изменениях рассылаются всем остальным клиентам, которые имеют у себя копии данного плана.
Рис. 12- 7. План движения поезда.
Этот механизм правильно работает и в том случае, когда изменения в план движения вносит диспетчер; при этом сначала изменяется копия плана в базе данных, а затем сообщения об изменениях рассылаются но сети остальным клиентам. Как в обоих случаях осуществляется передача изменении? Для этого мы используем механизм передачи сообщений, разработанный нами ранее. Заметим, что в результате проектирования мы добавили новое сообщение: изменение плана движения поезда. Таким образом, механизм передачи планов движения базируется на уже существующем низкоуровневом механизме передачи сообщений.
Использование готовой коммерческой СУБД на диспетчерских компьютерах позволит обеспечить резервирование данных, восстановление, ведение контрольного журнала и секретность информации.
Отображение информации
Использование готовых технологических решений для базы данных позволяет нам сосредоточиться на специфике задачи. Такого же результата можно добиться и в механизмах отображения информации, если использовать стандартные графические средства, например, Microsoft Windows или Х Windows. Использование готовых графических программных средств поднимает уровень абстракции нашей системы настолько, что разработчикам не надо беспокоиться об отображении информации на уровне пикселей. Кроме того, очень важно инкапсулировать проектные решения о графическом представлении различных объектов.
Рассмотрим, например, отображение информации о профиле и качестве участков пути. Требуется, чтобы изображение появлялось в двух местах: в диспетчерском центре и на поезде (где отображается путь только впереди поезда). Предполагая, что мы имеем некоторый класс, экземпляры которого представляют участки пути, можно рассмотреть два подхода к визуализации этого объекта. В соответствии с первым подходом, создается специальный объект, управляющий отображением, который преобразует состояние объекта в визуальную форму. Согласно второму подходу мы отказываемся от специального внешнего объекта и в каждый наш объект включаем информацию о том, как его отображать. Мы считаем, что второй подход предпочтительней, так как он проще и лучше отражает сущность объектной модели.
Однако, этот подход не лишен недостатков. Мы, вероятно, получим множество разновидностей отображаемых объектов, каждый из которых создан разными группами разработчиков. Если реализовывать каждый отображаемый объект отдельно, то возникают избыточный код. несогласованность стиля и вообще большая путаница. Правильнее проанализировать все разновидности отображаемых объектов, определить, какие у них общие элементы и создать набор промежуточных классов, который обеспечит отображение этих общих элементов. В свою очередь, промежуточные классы могут быть построены на основе коммерческих низкоуровневых графических пакетов.
Pиc. 12-8. Отображение информации.
На рис. 12-8 показано проектное решение о реализации всех отображаемых объектов с помощью общих утилит класса. Эти утилиты построены на основе низкоуровневого интерфейса Windows, который скрыт от всех высокоуровневых классов. На самом деле, процедуры Windows API трудно воплотить в одном классе или утилите. Наша диаграмма немного упрощена; вероятно, реализация потребует услуг нескольких классов Windows API и утилит отображения на дисплее компьютера в поезде.
Основное достоинство предлагаемого подхода заключается в том, что уменьшается влияние изменений, возникающих при перераспределении роли аппаратуры и программ. Например, если нам надо заменить наши дисплеи на более (менее) мощные, придется подправить процедуры только в классе TrainDisplayUtilities. Без такой декомпозиции нам бы пришлось вносить изменения в каждый отображаемый объект при любых изменениях на нижнем уровне.
Механизм опроса датчиков
Выше мы говорили, что система управления движением должна включать в себя большое количество разнообразных датчиков. Например, на каждом поезде датчики следят за температурой масла, количеством топлива, дроссельной установкой, температурой воды, нагрузкой на двигатель и т.д. Активные датчики путевых устройств сообщают текущее положение своих переключателей и передают сигналы. Все значения, возвращаемые датчиками - разные, но их обработка может производиться сходным образом. Допустим, что наш компьютер использует ввод-вывод по фиксированным адресам памяти. Тогда данные каждого датчика читаются из определенной области памяти и только потом интерпретируются способом, зависящим от конкретного датчика. Большинство датчиков должно опрашиваться периодически. Если значение находится в заданных пределах, оно сообщается какому-то клиенту, и больше ничего не происходит. Если же отсчет датчика вышел за установленные пределы, об этом могут быть оповещены и другие клиенты. Наконец, если отсчет вышел далеко за допустимые границы (например, давление масла на локомотиве поднимается до опасного уровня), может понадобиться какой-то звуковой сигнал тревоги и уведомление специальных клиентов для принятия решительных мер.
Воспроизведение этого поведения для каждого датчика не только утомительно и чревато ошибками, но и раздувает объем кода. Если мы с самого начала не выделим общие для всех датчиков характеристики, то различные разработчики предложат свои решения одной и той же задачи. Это, в свою очередь, приведет к сложностям при сопровождении системы. Поэтому для выявления общих свойств необходимо провести анализ всех периодически опрашиваемых аналоговых датчиков и предложить общий механизм их опроса, приемлемый для всех.
Мы уже решали аналогичную задачу в главе 8, применительно к метеорологической станции. Там мы создали иерархию классов датчиков и описали механизм их опроса. Есть все основания воспользоваться полученным ранее решением и в нашей нынешней задаче.
Это хороший пример повторного использования проектных решении в различных прикладных областях.
12.3. Эволюция
Модульная архитектура
Мы уже говорили о том, что модульность для больших систем необходима, но не достаточна; для задач такого масштаба, как система управления движением, нужно сосредоточиться на декомпозиции по подсистемам. На ранних стадиях эволюции мы должны разработать модульную архитектуру системы, представляющую физическую структуру ее программного обеспечения.
Проектирование программного обеспечения для очень больших систем должно начинаться до полного завершения проектирования аппаратных средств. Написание программы занимает, как правило, даже больше времени, чем разработка аппаратуры. Кроме того, по ходу процесса функциональность может перераспределяться между аппаратной и программной частями. Поэтому зависимость от аппаратуры должна быть максимально изолирована, так, чтобы программные средства можно было начать проектировать без привязки к аппаратуре. Это означает также, что разработка должна основываться на идее взаимозаменяемых подсистем. В системах управления и контроля, таких, как система управления движением, нужно сохранить возможность задействовать новые аппаратные решения, которые могут появиться в процессе разработки программного обеспечения.
На ранних этапах мы должны разумно провести декомпозицию программного обеспечения, чтобы субподрядчики, ответственные за различные части системы, могли работать одновременно (возможно даже используя различные языки программирования). Как уже говорилось в главе 7, существует много причин нетехнического характера, определяющих физическую декомпозицию больших систем. Наиболее важен вопрос взаимодействия различных групп разработчиков. Отношения между субподрядчиками складываются обычно на достаточно ранних стадиях жизни системы, часто до получения информации, достаточной для выбора правильной декомпозиции системы.
Желательно, чтобы системные архитекторы поэкспериментировали с несколькими альтернативными декомпозициями на подсистемы для того, чтобы быть уверенными в правильности глобального решения по физическому проектированию. Можно задействовать прототипирование в больших масштабах с имитацией подсистем и моделированием загрузки процессора, маршрутизации сообщений и внешних событий. Прототипирование и моделирование могут послужить основой для нисходящего тестирования по мере создания системы.
Как выбрать подходящую декомпозицию на подсистемы? В главе 4 отмечено, что объекты на высоком уровне абстракции обычно группируются в соответствии с их функциональным повелением. Еще раз подчеркнем, что это не противоречит объектной модели, так как термин функциональный мы не связываем жестко с понятием алгоритма. Мы говорим о функциональности как о внешнем видимом и тестируемом поведении, возникающем в результате совместной деятельности объектов. Таким образом, абстракции высокого уровня и механизмы, о которых говорилось ранее, хорошо подходят на роль подсистем. Мы можем сначала допустить существование таких подсистем, а их интерфейс разработать через некоторое время.
На диаграмме модулей на рис. 12-9 представлены проектные решения верхнего уровня модульной архитектуры системы управления движением. Каждый уровень здесь соответствует выделенным ранее четырем подзадачам: сеть передачи данных, база данных, аналоговые устройства управления в реальном времени, интерфейс "человек/компьютер".
Рис. 12-9. Диаграмма модулей верхнего уровня системы управления движением.
Спецификация подсистем
Если мы рассмотрим внешнее представление любой из подсистем, то обнаружим, что она обладает всеми характеристиками объекта. Каждая подсистема имеет уникальную, хотя и статичную, идентичность и большое число возможных состояний, а также демонстрирует очень сложное поведение. Подсистемы используются как хранилища других классов, утилит классов и объектов; таким образом, они лучше всего характеризуются экспортируемыми ресурсами. На практике при использовании C++ эти ресурсы представляются в форме каталогов, содержащих логически связанные модули и вложенные подсистемы.
Диаграмма модулей на рис. 12-9 полезна, но не полна, так как каждая из подсистем на ней слишком велика для реализации одним небольшим коллективом разработчиков. Мы должны раскрыть внутреннее представление подсистем верхнего уровня и провести их декомпозицию.
Рассмотрим для примера подсистему NetworkFacilities (сеть). Мы решили разбить ее на две другие подсистемы, одна из которых - закрытая (RadioCommunication (радиосвязь)), а другая - открытая (Messages (сообщения)). Закрытая подсистема скрывает детали своего программного управления физическими устройствами, в то время как открытая подсистема обеспечивает поддержку спроектированного ранее механизма передачи сообщений.
Подсистема, названная Databases (базы данных), построена на основе ресурсов подсистемы NetworkFacilities и служит для реализации механизма планов движения поезда, который мы описали выше. Мы составляем эту подсистему из двух экспортируемых открытых подсистем, TrainPlanDatabase (база данных планов поездов) и TrackDatabase (база данных путей). Для действий, общих для этих двух подсистем, мы предусмотрим закрытую подсистему DatabaseManager (менеджер баз данных).
Подсистема Devices (устройства) также естественно разбивается на несколько небольших подсистем. Мы решили сгруппировать программы, относящиеся ко всем путевым устройствам, в одну подсистему, а программы, связанные с активными механизмами и датчиками локомотива, - в другую. Эти две подсистемы доступны клиентам подсистемы Devices, и обе они построены на основе ресурсов подсистем TrainPlanDatabase и Messages. Таким образом, мы спроектировали подсистему Devices для реализации механизма датчиков, который описан выше.
Наконец, мы представляем подсистему верхнего уровня UserApplications (прикладные программы) в виде нескольких небольших подсистем, включая EngineerApplications (программы для машиниста) и DispatcherApplications (программы для диспетчера), чтобы зафиксировать разную роль двух главных пользователей системы управления движением. Подсистема EngineerApplications содержит ресурсы, которые обеспечивают взаимодействие машиниста и компьютера, в частности, анализ системы сбора и отображения информации о состоянии локомотива и системы управления энергией. Подсистема DispatcherApplicatlona обеспечивает интерфейс "диспетчер/компьютер". Подсистемы EngineerApplications и DispatcherApplications разделяют общие закрытые ресурсы, экспортируемые из подсистемы Displays (отображение), которая реализует описанный ранее механизм отображения.
В результате проектирования мы получили четыре подсистемы верхнего уровня и десять подсистем следующего уровня, в которых размещены все введенные ранее ключевые абстракции и механизмы. Важно, что в терминах этих подсистем можно планировать работу, управлять конфигурациями и версиями. Как говорилось в главе 7, отвечать за каждую такую подсистему может один человек, в то время как разрабатывать ее будет множество программистов. Ответственный за подсистему детализирует ее проект и реализацию и управляет ее интерфейсом с другими подсистемами на том же уровне абстракции. Так, за счет декомпозиции сложной задачи на несколько более простых, становится возможным управление разработкой сложных проектов.
В главе 7 уже демонстрировалась возможность нескольких одновременных представлений разрабатываемой системы. Набор совместимых версий подсистем образует релиз, и таких релизов может быть множество - по одному на каждого разработчика, еще один - для тестирования, один - для опробования пользователями и т.д. Отдельные проектировщики могут для своих нужд создавать собственные стабильные релизы и интегрировать в них те части, за которые они отвечают, до передачи их остальным. Так создается механизм непрерывной интеграции нового кода.
Основой успеха является тщательное конструирование интерфейсов подсистем. После того как интерфейсы определены, они должны тщательно оберегаться. Как мы определяем внешнее представление подсистемы? Нужно каждую подсистему рассматривать как объект. Поэтому мы ставим те же вопросы, которые задавали в главе 4 для значительно более простых объектов: Какие состояния имеет объект? Какие действия над ним может выполнить клиент? Каких действий он требует от других объектов?
Например, рассмотрим подсистему TrainPlanDatabase. Она строится на основе трех других подсистем (Messages, TrainDatabase, TrackDatabase) и имеет нескольких важных клиентов - подсистемы WaysideDevices (путевые устройства), LocomotiveDevices (устройства на локомотиве), EngineerApplications и DispatcherApplications. Подсистема TrainPlanBatabase относительно проста - она содержит все планы поездов. Конечно, хитрость в том, что эта подсистема должна поддерживать механизм распределенной передачи планов движением поезда. Снаружи клиент видит монолитную базу данных, но изнутри мы знаем, что на самом деле база данных - распределенная, и поэтому должны основывать се на механизме передачи сообщений подсистемы Messages.
Какие действия можно выполнять с помощью TrainPlanDatabase? Все обычные для базы данных операции: добавление, удаление и изменение записей, запросы. Так же как в главе 10, нужно зафиксировать все проектные решения об этой подсистеме в форме классов C++, которые снабдят нас объявлениями операций.
На этой стадии нам следует продолжить процесс проектирования для каждой подсистемы. Еще раз отметим, что вероятность того, что все интерфейсы окажутся правильными с первого раза, очень мала. К счастью, как и для небольших объектов, опыт подсказывает, что большинство изменений, которые мы произведем в интерфейсах, не затронет верхних уровней (совместимость снизу вверх), если мы хорошо поработали, описывая каждую подсистему в объектно-ориентированном стиле.
12.4. Сопровождение
Добавление новых функций
Программное обеспечение сопровождается и постоянно дорабатывается, что особенно справедливо для таких больших систем, как наша. Действительно, до сих пор можно встретить программы, разработанные лет двадцать назад (просто патриархальные по компьютерным меркам). Чем больше пользователей применяет систему управления движением и чем лучше мы адаптируем проект к новым требованиям, тем чаще клиенты будут находить новые неожиданные применения для существующих механизмов, создавая потребность во включении в систему новых функций.
Рассмотрим единственное добавление к нашим требованиям: обработку платежной ведомости. Предположим, анализ показал, что работа с платежными ведомостями железнодорожной компании осуществляется с использованием аппаратуры, выпуск которой прекращен, поэтому возник серьезный риск безвозвратной потери всей системы платежей в результате нескольких критических поломок. В этом случае можно объединить обработку платежной ведомости с системой управления движением. Для начала надо понять, как эти две несвязанные задачи будут сосуществовать; можно рассматривать их как разные приложения, причем обработка платежной ведомости будет происходить в фоновом режиме.
Дальнейший анализ показывает, что от интеграции обработки платежной ведомости может быть получена огромная польза. Вспомним, что планы поездов содержат информацию о распределении бригад. Следовательно, мы можем проанализировать запланированное и действительное распределение бригад, вычислить рабочее время, сверхурочные часы и т. п. Получая эту информацию непосредственно, мы можем обрабатывать платежную ведомость дешевле и быстрее.
Что добавление этой функции затрагивает в нашем проекте? Очень немногое. Новую подсистему можно добавить в подсистему UserApplications. Оттуда новой подсистеме будут видны все важные механизмы, которые нужны для ее функционирования. Признак хорошо спроектированной объектно-ориентированной системы: значительные дополнения к требованиям могут быть учтены довольно просто путем надстройки новых функций над существующими механизмами.
Предположим, мы хотим ввести более существенное изменение: добавить экспертную систему, помогающую диспетчеру при определении маршрутов и реагирующую на чрезвычайные ситуации. Как это требование отразится на нашем проекте? Незначительно. Мы можем разместить новую подсистему между подсистемами TrainPlanDatabase и DispatcherApplications, так как база знаний, созданная для экспертной системы, подобна по содержанию TrainPlanDatabase; кроме того, подсистема DispatcherApplications является единственным клиентом экспертной системы. Нам предстоит разработать некоторый новый механизм, чтобы доводить рекомендации до конечного пользователя. Например, мы можем использовать метафору информационной доски, как это делалось в главе 11.
Изменение аппаратных средств
Мы уже говорили, что аппаратные средства развиваются быстрее, чем программное обеспечение. Более того, всегда будут причины, вынуждающие нас выбрать в ходе проектирования такие аппаратные решения, о которых потом мы будем сожалеть [Например, часть аппаратуры придется закупить у третьей стороны, а потом обнаружится, что поставленная продукция не отвечает оговоренным условиям. Или даже хуже того: поставщик критически важной продукции вышел из бизнеса. В таких случаях менеджер проекта должен выбрать одно из двух: (1) стенать в ночи; (2) подыскать замену и надеяться, что архитектура системы достаточно гибка, чтобы приспособиться к изменениям. Объектно-ориентированные анализ и проектирование помогут нам достигнуть (2), хотя иногда очень утешительно прибегнуть к (1)]. Поэтому рабочая аппаратура в больших системах устаревает гораздо раньше программы. Например, после нескольких лет эксплуатации мы можем заменить дисплеи на всех поездах и во всех диспетчерских центрах. Как это может повлиять на существующий проект? Если во время разработки мы сохраняли интерфейсы подсистем па высоком уровне абстракции, это изменение аппаратуры приведет лишь к незначительным изменениям в программе. Мы подправим только совокупность процедур, относящуюся к дисплеям, не затрагивая другие подсистемы, которые вообще ничего не знают об особенностях конкретных рабочих станций. Это достигается благодаря тому, что поведение всех рабочих станций скрыто в подсистеме Displays. Таким образом, подсистема действует как стена абстракций, которая защищает остальных клиентов от наших трудностей, вызванных разнообразием дисплеев.
Аналогично, радикальные изменения в стандартах телекоммуникации затронут нашу реализацию в очень ограниченном отношении. Наш проект гарантирует, что только подсистема Messages связана с сетевыми коммуникациями. Таким образом, фундаментальные изменения в сети не отразятся ни на каком высокоуровневом клиенте; подсистема Messages защищает их от капризов сетевой моды.
Итак, воображаемые изменения, которые мы вводили, не смогли разрушить структуру созданного нами проекта. Это - верный признак хорошо спроектированной объектно-ориентированной системы.
Дополнительная литература
Требования к системе управления движением основываются на Продвинутой системе управления поездами (Advanced Train Control System), описанной Марфи (Murphy) [С 1988].
Передача и проверка сообщений присутствует практически во всех системах управления и контроля. Плинта, Ли и Риссман (Plinta, Lee, and Rissman) [С 1989] дали блестящее изложение этих вопросов и предложили механизм передачи сообщений по процессорам в распределенной системе, безопасный с точки зрения типов.
|