Абстрактные типы данных (АТД)
Переход к более императивной точке зрения
Переход от АТД к классам включает существенное изменение стилистики: введение изменений и императивных аргументов.
Как вы помните, спецификация абстрактных типов данных не описывает явно изменений, т. е., используя термин из теоретической информатики, является аппликативной. Все свойства АТД моделируются как математические функции, это относится к конструкторам, запросам и командам. Например, операция вталкивания для стеков моделируется функцией-командой:
put: STACK [G] x G -> STACK [G],
задающей операцию, которая возвращает новый стек, а не изменяет существующий.
Классы отказываются от чисто аппликативной точки зрения на функции и переопределяют команды как операции, которые могут изменять объекты. Например, операция put будет определена как процедура, которая получает некоторый элемент типа G (формальный параметр) и модифицирует стек, вталкивая новый элемент на его вершину, не создавая нового стека.
Такое изменение стиля отражает императивные настроения, преобладающие при разработке ПО. (В качестве синонима слова "императивный" иногда используется термин "операционный"). При этом потребуется изменять аксиомы АТД. Аксиомы стеков A1 и A4, которые имели вид
- (A1) item (put (s, x)) = x
- (A4) not empty (put (s, x))
превратятся в императивной форме в предложение, называемое постусловием программы (routine postcondition), вводимое ключевым словом ensure (обеспечивает):
put (x: G) is -- Втолкнуть x на вершину стека require ... Предусловие (если таковое имеется) ... do ... Соответствующая реализация (если известна) ... ensure item = x not empty end
Здесь постусловие объясняет, что результатом вызова программы put значение item будет равно x (втолкнутому элементу), а значение empty будет ложно.
Другие аксиомы спецификации АТД приводят к утверждению, известному как инвариант класса. Постусловия, инварианты класса и другие перевоплощения предусловий и аксиом АТД мы рассмотрим во время обсуждения утверждений и проектирования по контракту (п. 11.10 "Связь с АТД").
Назад к тому, с чего начали?
Если вы внимательно следили, начиная с лекции о модульности, за главной линией рассуждений, которая привела нас к абстрактным типам данных, а затем и к классам, то сейчас, быть может, вы будете удивлены. Поставив целью получить по возможности наилучшую модульную структуру, мы пришли к тому, что объекты, точнее - типы объектов, будут лучшей основой для модулей, чем их традиционные соперники - функции. Это привело к следующему вопросу: как описать эти типы объектов. Но, когда мы на него ответили: описывать нужно в виде абстрактных типов данных (и их заменителей на практике - классов), то оказалось, что нужно основывать описание данных на ... применяемых к ним функциях! Не получился ли у нас порочный круг?
Нет. Типы объектов, представлямые АТД и классами, остаются неизменной основой модуляризации.
Неудивительно, что и объектный, и функциональный аспект должен проявиться в окончательной архитектуре системы: никакое описание вопросов ПО не может считаться полным, если в нем опущена одна из этих компонент. Фундаментальное различие ОО-методов и старых подходов состоит в распределении ролей: типы объектов - безусловные победители при выборе критериев для построения модулей. Функциям достается только роль их слуг.
При ОО-декомпозиции никакая функция не существует сама по себе - каждая функция прикреплена к некоторому типу объектов. Это относится и к уровню проектирования, и к уровню разработки: никакое свойство не существует само по себе, каждое из них прикреплено к некоторому классу.
Конструирование объектно-ориентированного ПО
Мы уже давали определение конструирования ОО-ПО: будучи весьма общим, оно представляет метод следующим образом: "основывать архитектуру всякой программной системы на модулях, полученных из типов объектов, с которыми оперирует система". Придерживаясь рамок этого определения, мы можем дополнить его теперь более техническим определением:
Конструирование объектно-ориентированного ПО (определение 2)
Конструирование ОО-ПО - это построение программной системы как структурированной совокупности реализаций (возможно частичных) абстрактных типов данных.
Это определение будет нашим рабочим определением. Все его компоненты являются важными:
- В основе лежит понятие абстрактного типа данных.
- Для конструирования программ нам нужны не сами по себе АТД (как математическое понятие), а реализации АТД - программистское понятие.
- При этом эти реализации не обязаны быть полными, оговорка "возможно частичные" позволяет использовать и отложенные классы, включая, как крайний случай, полностью отложенный класс без какой-либо реализации.
- Система представляет собой совокупность классов без выделения какого-либо главного или ответственного класса или головной программы.
- Эта совокупность является структурированной благодаря двум отношениям между классами: "быть клиентом" и наследованию.