Основы объектно-ориентированного программирования - [42]

Шрифт
Интервал

>t: INTEGER_TABLE_HANDLING$INTBINTREE

>-- Инициализация t новой таблицей, создаваемой функцией new пакета:

>t := INTEGER_TABLE_HANDLING$new

>-- Включение x в таблицу, используя процедуру put пакета:

>INTEGER_TABLE_HANDLING$put (t, x)

>-- Присваивание True или False переменной b,

>-- для поиска используется функция has пакета:

>b := INTEGER_TABLE_HANDLING$has (t, x)


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

Менее важной проблемой является утомительная необходимость неоднократно писать имя пакета (здесь это INTEGER_TABLE_HANDLING). В языках, поддерживающих работу с пакетами, эта проблема решается с помощью различных сокращенных синтаксических конструкций (shortcuts), таких как, например, в языке Ada:

>with INTEGER_TABLE_HANDLING then

>... Здесь has означает INTEGER_TABLE_HANDLING$has, и т.д. ... end


Другим очевидным недостатком пакетов рассмотренного вида является их неспособность удовлетворять требованию Изменчивости Типов: приведенный выше модуль пригоден лишь для таблиц целых чисел. Однако, вскоре мы увидим, как устранить этот недостаток, делая пакеты универсальными (generic).

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

Языки, поддерживающие работу с пакетами, несколько различаются своими механизмами скрытия информации. Например, в языке Ada, внутренние свойства типа, такого как INTBINTREE, будут доступны клиентам, если не объявить тип как private (закрытый).

Часто для усиления скрытия информации в языках с инкапсуляцией предлагается объявлять пакет, состоящий из двух частей, интерфейса (interface) и реализации (implementation)(См. лекция 11 и лекция 5 курса "Основы объектно-ориентированного проектирования"). Закрытые элементы, такие как объявление типа или тело подпрограммы, включаются в раздел реализации. Однако такой подход приводит к добавочной работе для разработчиков модулей, заставляя их дублировать заголовки объявлений компонентов. При глубоком осмыслении правила Скрытия Информации все это не требуется. Подробнее эта проблема обсуждается в последующих лекциях.

Пакеты: оценка

По сравнению с подпрограммами, механизм пакетов приводит к существенному совершенствованию разбиения системы ПО на абстрактные модули. Собрать нужные компоненты "под одной крышей" крайне полезно как для поставщиков, так и для клиентов:

[x]. Автор модуля-поставщика может хранить в одном месте и совместно компилировать все элементы, относящиеся к некоторому заданному понятию. Это облегчает отладку и изменения. В отличие от этого, при использовании отдельных самостоятельных подпрограмм всегда есть опасность забыть произвести обновление некоторых подпрограмм при изменениях проекта или реализации; например, можно обновить new, put и has, но забыть обновить remove.

[x]. Для авторов модулей-клиентов несомненно легче найти и использовать множество взаимосвязанных компонентов, если все они собраны в одном месте.

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

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

Но даже со стороны клиентов ситуация остается не вполне приемлемой. Каждое использование таблицы клиентом требует упомянутого выше объявления вида: