Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4086 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 2:

Образец проектирования: многопанельные интерактивные системы

< Лекция 1 || Лекция 2: 123456 || Лекция 3 >

Объектно-ориентированная архитектура

Проблемы функциональной декомпозиции сверху вниз указывают, что нужно делать, чтобы получить хорошую ОО-версию.

Закон инверсии

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

Закон инверсии

Если программы пересылают друг другу много данных, поместите программы в данные.

Ранее модули строились вокруг операций (таких как execute_session и execute_state ) и данные распределялись между программами со всеми неприятными последствиями, с которыми нам пришлось столкнуться. ОО-проектирование ставит все с головы на ноги, - оно использует наиболее важные типы данных как основу модульности, присоединяя каждую программу к тому типу данных, с которым она наиболее тесно связана. Когда объекты одерживают победу, их бывшие хозяева - функции - становятся вассалами данных.

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

Конечно, желательно начинать ОО-проектирование с самого начала, - тогда не придется прибегать к инверсии. Но закон инверсии полезен не только в случае реверс-инженерии или для новичков-разработчиков. И в объектной разработке проекта на фоне объектного ландшафта могут встречать области функциональной декомпозиции. Анализ передачи данных является хорошим способом обнаружения и корректировки подобных дефектов. Если вы обнаружили в структуре, разрабатываемой как объектной, образцы передачи данных, подобные состояниям в данном примере, то это должно привлечь ваше внимание и привести в большинстве случаев к осознанию абстракции данных, не нашедшей еще должного места в проекте.

Состояние как класс

Пример "состояния" является типичным. Такой тип данных, играющий всеобъемлющую роль в передаче данных между программами, является первым кандидатом на роль модуля в ОО-архитектуре, основанной на классах (абстрактно описанных типах данных).

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

В этом классе мы найдем все операции, характеризующие состояние: отображение соответствующего экрана ( display ), анализ ответа пользователя ( read ), проверку ответа ( correct ), выработку сообщения об ошибке для некорректных ответов ( message ), обработку корректных ответов ( process ). Мы должны также включить сюда execute_state, выражающее последовательность действий, выполняемых всякий раз, когда сессия достигает заданного состояния (поскольку данное имя было бы сверхквалифицированным в классе, называемом STATE, заменим его именем execute ).

Возвращаясь к рисунку, отражающему функциональную декомпозицию, выделим в нем множество программ, принадлежащих классу STATE.

Компоненты класса STATE

Рис. 2.6. Компоненты класса STATE
Класс имеет следующую форму:
...class STATE feature
    input: ANSWER
    choice: INTEGER
    execute is do ... end
    display is ...
    read is ...
    correct: BOOLEAN is ...
    message is ...
    process is ...
end

Компоненты input и choice являются атрибутами, остальные - подпрограммами (процедурами и функциями). В сравнении со своими двойниками при функциональной декомпозиции подпрограммы потеряли явный аргумент, задающий состояние, хотя он появится другим путем в клиентских вызовах, таких как s.execute.

В предыдущих подходах функция execute (ранее execute_state ) возвращала пользовательский выбор следующего шага. Но такой стиль нарушает правила хорошего проектирования. Предпочтительнее сделать execute командой. Запрос "какой выбор сделал пользователь в последнем состоянии?" доступен благодаря атрибуту choice. Аналогично, аргумент ANSWER подпрограмм уровня 1 заменен теперь закрытым атрибутом input. Вот причина скрытия информации: клиентскому коду нет необходимости обращаться к ответам помимо интерфейса, обеспечиваемого компонентами класса.

Наследование и отложенные классы

Класс STATE описывает не частное состояние, а общее понятие состояния. Процедура execute - одна и та же для всех состояний, но другие подпрограммы зависят от состояния.

Наследование и отложенные классы идеально позволяют справиться с этими ситуациями. На уровне описания класса STATE мы знаем атрибуты и процедуру execute во всех деталях. Мы знаем также о существовании программ уровня 1 ( display и др.) и их спецификации, но не их реализации. Эти программы должны быть отложенными, класс STATE, описывающий множество вариантов, а не полностью уточненную абстракцию, сам является отложенным классом. В результате имеем:

indexing
        description: "Состояния приложений, управляемых панелями"
deferred class
    STATE
feature -- Access
    choice: INTEGER
        -- Пользовательский выбор следующего шага
    input: ANSWER
        -- Пользовательские ответы на вопросы в данном состоянии
feature -- Status report
    correct: BOOLEAN is    
        -- Является ли input корректным ответом?
        deferred
        end
feature -- Basic operations
    display is
        -- Отображает панель, связанную с текущим состоянием
        deferred
        end
    execute is
            -- Выполняет действия, связанные с текущим состоянием,
            -- и устанавливает choice - пользовательский выбор
        local
            ok: BOOLEAN
        do
            from ok := False until ok loop
                display; read; ok := correct
                if not ok then message end
            end
            process
        ensure
            ok
        end
    message is
            -- Вывод сообщения об ошибке, соответствующей input
        require
            not correct
        deferred
        end
    read is
            -- Получить ответы пользователя input и choice
        deferred
        end
    process is
            -- Обработка input
        require
            correct
        deferred
        end
end

Для описания специфических состояний следует ввести потомков класса STATE, задающих отложенную реализацию компонент.

Иерархия классов State

Рис. 2.7. Иерархия классов State

Пример мог бы выглядеть следующим образом:

class ENQUIRY_ON_FLIGHTS inherit
    STATE
feature
    display is
        do
            ...Специфическая процедура вывода на экран...
        end
    ...И аналогично для read, correct, message и process ...
end

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

Класс STATE является типичным представителем поведенческих классов ( behavior classes, см. лекцию 14 курса "Основы объектно-ориентированного программирования") - отложенных классов, задающих общее поведение большого числа возможных объектов, реализующих то, что полностью известно на общем уровне ( execute ) в терминах, зависящих от каждого варианта. Наследование и отложенный механизм задают основу представления такого поведения повторно используемых компонентов.

< Лекция 1 || Лекция 2: 123456 || Лекция 3 >