Объектно-ориентированное проектирование с примерами

       

Механизм передачи сообщений


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

    между компьютерами и устройствами

    между компьютерами.

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

    Первый шаг при определении сообщений в системе - анализ взаимодействия каждой пары сообщающихся компьютеров (см. рис. 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]. Это позволит всем клиентам, передатчикам и приемникам работать на самом высоком уровне абстракции и общаться друг с другом в терминах, специфических для данного приложения.

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


    Содержание раздела