Механизм передачи сообщений
Под сообщением здесь мы не имеем в виду активизацию методов, как это принято в объектно- ориентированных языках программирования. В данном случае понятие взято из словаря предметной области, из самого высокого уровня абстракции. Вот несколько примеров сообщений в системе управления движением: сигнал запуска путевому устройству, сообщение о прохождении поезда через определенный пункт пути, приказ диспетчера машинисту. Все эти виды сообщений могут передаваться внутри системы управления движением на двух уровнях:
• между компьютерами и устройствами
• между компьютерами.
Сейчас нас интересует второй уровень передачи сообщений. Так как система включает территориально распределенную сеть, мы должны учесть такие факторы, как помехи, отказы оборудования и секретность передачи информации.
Первый шаг при определении сообщений в системе - анализ взаимодействия каждой пары сообщающихся компьютеров (см. рис. 12-3). Для каждой такой пары мы должны задать три вопроса: (1) Какую информацию обрабатывает каждый компьютер? (2) Какая информация будет передаваться с одного компьютера на другой? (3) К какому уровню абстракции будет относиться эта информация? Эмпирического ответа на эти вопросы нет. Мы должны действовать итеративно, пока не придем к уверенности, что определены правильные сообщения и в системе связи нет 'узких' мест (которые могут возникать из-за перегрузки линий связи или, например, из-за того, что сообщение разбивается на слишком мелкие пакеты).
Очень важно, чтобы на данном этапе проектирования внимание было сосредоточено на сути, а не на форме сообщений. Слишком часто системные архитекторы начинают проектирование с выбора битового представления сообщений. В реальной задаче преждевременный выбор низкоуровневого представления обязательно приведет к изменениям в дальнейшем и затронет всех, кто пользовался этим представлением. Кроме того, на ранней стадии проектирования у нас пока нет полной информации, как будут использоваться данные сообщения, и, следовательно, мы не можем судить, какое представление будет оптимальным по размеру и времени передачи.
Концентрируя внимание на сути сообщений, мы рассмотрим все классы сообщений. Другими словами, нужно определить назначение и смысл каждого сообщения, а также перечислить операции их обработки.
На диаграмме классов на рис. 12-4 показаны некоторые наиболее важные сообщения в системе управления движением. Заметим, что все сообщения в конечном счете являются экземплярами абстрактного класса Message, который инкапсулирует поведение, общее для всех сообщений. Три класса следующего уровня представляют главные категории сообщений: сообщение о состоянии поезда, сообщение о плане движения поезда, сообщение путевого устройства. Каждый из этих трех классов будет детализирован в дальнейшем. В результате проектирования должны появиться десятки специализированных классов. Таким образом, существование обобщающих абстрактных классов чрезвычайно важно; без них мы получили бы сотни несвязанных между собой и, следовательно, сложных в использовании модулей, каждый из которых реализовывал бы специализированный класс, По мере проектирования мы будем выявлять другие важные группы сообщений и создавать для них специализированные промежуточные классы. К счастью, изменения в иерархии классов не должны волновать клиентов, использующих классы.
Прежде всего нам следует стабилизировать интерфейсы ключевых классов сообщений. Начинать этот процесс лучше всего с основных классов иерархии. Начнем с введения двух следующих типов;
//номер, обозначающий уникальный идентификатор пакета 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 показан результат проектирования механизма передачи сообщений. Как видно на диаграмме, чтобы послать сообщение, клиент сначала создает новое сообщение м, затем передает его диспетчеру своего узла, который ставит сообщение в очередь для последующей отправки. Заметьте, что наш проект предусматривает для клиента возможность ожидания, если диспетчер узла не может осуществить отправку вовремя. Диспетчер получает сообщение как параметр и затем пользуется услугами объекта Transporter (передатчик), который обеспечивает необходимый формат сообщения и его рассылку по сети.
Мы сделали эту операцию асинхронной, чтобы клиент не ждал, пока сообщение будет отправлено по радио, что требует времени для кодирования, декодирования и повторных передач из-за помех. В конечном счете объект Listener (слушатель) принимает сообщение, преобразует его в принятую форму для диспетчера своего узла, который создает параллельное сообщение и ставит его в очередь. Получатель может заблокировать начало очереди сообщений, ожидая прихода следующего сообщения, которое передается как параметр синхронной операции nextMessage.
При проектировании диспетчера мы располагаем его на прикладном уровне сетевой модели ISO OSI [4]. Это позволит всем клиентам, передатчикам и приемникам работать на самом высоком уровне абстракции и общаться друг с другом в терминах, специфических для данного приложения.
Мы ожидаем, что окончательная реализация описанного механизма будет, вероятно, несколько более сложной. Например, может потребоваться шифровать и дешифровать сообщение и использовать коды обнаружения и исправления ошибок, чтобы обеспечить надежную связь в условиях помех на линиях связи и возможных отказов оборудования.