24.2. Абстракция данных при помощи экзистенциальных типов 291
Упражнение 24.2.3 Рекомендуется, : Реализуйте переключатели FlipFlop как объекты, ис-
пользуя в качестве типа внутреннего представления Counter. В качестве образца можно взять АТД
FlipFlop из предыдущего подраздела.
Упражнение 24.2.4 Рекомендуется, : При помощи интерпретатора fullomega реализуйте им-
перативный вариант объектов Counter по образцу Упражнения 24.2.2.
24.2.3. Сравнение объектов и АТД
Примеры из предыдущего раздела не составляют полноценную модель объектно-ориентированного
программирования. Многие характеристики объектной ориентированности, рассмотренные нами в Гла-
вах 18 и 19, в том числе наследование типов и реализаций, классы, рекурсия через self и super, здесь
отсутствуют. Мы вернемся к моделированию этих характеристик в Главе 32, когда наша система ти-
пов будет обогащена некоторыми существенными чертами. Однако уже сейчас мы можем провести
интересное сравнение наших простых объектов с обсужденными ранее АТД.
При самом поверхностном взгляде эти два типа программистских идиом лежат на противополож-
ных концах спектра: в процессе программирования через АТД пакеты открываются немедленно после
создания; с другой стороны, когда пакеты используются для построения объектов, они остаются за-
крытыми как можно дольше — до момента, когда их приходится открывать, чтобы применить один
из методов к внутреннему состоянию объекта.
Вследствие этого различия «абстрактный тип счетчиков» в этих двух стилях означает разные вещи.
В программе, написанной в стиле с АТД, значения счетчиков, с которыми работает клиентский код
вроде функции add3, являются элементами внутреннего типа представления (т. е., обыкновенными
числами). В объектной программе каждый счетчик представляет собой отдельный пакет — включая
не только число, но и представления методов get и inc. Это стилистическое различие отражается в
том, что в стиле с АТД тип Counter является связанной переменной, вводимой в конструкции let, а в
объектном стиле Counter обозначает весь экзистенциальный тип
{ X , { state :X , method s :{ get :X -> Nat , inc :X ->X }}}
Таким образом, во время исполнения все значения счетчиков, порожденные АТД — это просто неупа-
кованные элементы одного и того же типа представления, и существует единственная реализация опе-
раций со счетчиками, которая работает с этим внутренним представлением. Напротив, каждый объект-
счетчик несет свой тип представления и свой собственный набор методов, работающих с этим типом
представления.
Эти расхождения между АТД и объектами приводят к различным практическим достоинствам и
недостаткам. Одно очевидное отличие состоит в том, что, поскольку каждый объект сам выбирает свое
представление и несет свои собственные операции, в одной программе можно работать с несколькими
представлениями одного и того же типа объектов. Это особенно удобно, когда в системе присутствует
наследование: можно определить один общий тип объектов, а затем породить множество уточнений,
каждое из которых обладает своим несколько (или сильно) отличным внутренним представлением.
Поскольку экземпляры этих уточненных классов все имеют один и тот же общий тип, с ними может
работать один общий код, их можно совместно хранить в списках и т. п.
Например, библиотека пользовательского интерфейса может определить общий класс Window
(окно) с подклассами вроде TextWindow (текстовое окно), ContainerWindow (окно-контейнер),
ScrollableWindow (окно с полосами прокрутки), TitledWindow (окно с заголовком), DialogBox (диало-
говое окно) и т. д. В каждом из этих подклассов будут собственные переменные экземпляра (например,
TextWindow может содержать переменную типа String, представляющую текущее содержимое окна, а
ContainerWindow может хранить список объектов типа Window), и у него будут свои собственные реали-
зации для операций вроде repaint (перерисовка) и handleMouseEvent (обработка события с мышью).
С другой стороны, определение типа Window как АТД ведет к менее гибкой структуре. Конкретный
тип представления Window должен будет содержать вариантный тип (§11.10) с вариантом для каж-
дой разновидности окна, и к этому варианту будут приписаны данные, относящиеся именно к этой
разновидности. Операции вроде repaint будут содержать case по вариантам и выполнять соотвеству-
ющий специализированный код. Если разновидностей окон существует много, монолитное объявление
абстрактного типа Window может стать слишком большим и запутанным.
rev. 104