Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP / books / Osnovi objektno-orientirovannogo programmirovaniya.pdf
Скачиваний:
62
Добавлен:
03.03.2016
Размер:
9.04 Mб
Скачать

Стоимость универсализации

Как всегда нужно убедиться, что ОО-техника, введенная в интересах повторного использования, расширяемости и надежности, не влечет потерю производительности. Этот вопрос уже поднимался при рассмотрении массивов. Теперь необходимо с этих позиций проэкзаменовать механизм универсализации в целом. Какова цена универсализация?

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

|Число создаваемых экземпляров шаблона - уже проблема для некоторых пользователей С++. Если пользователь создает List<int>, List<String>, List<Widget> и List<Blidget> (где Widget и Blidget классы, определенные пользователем) и вызывает head, tail и insert для всех четырех объектов, то каждая из этих функций будет создана в четырех экземплярах (из-за родового порождения). Вместо этого широко применимый класс List мог бы создать единственный

экземпляр каждой функции применимый для различных типов10.5) |

Авторы этого предупреждения (С++ эксперты из AT&T, один из них соавтор официальной С++ документации [Ellis 1990]) продолжают предлагать различные способы, позволяющие избежать порождения шаблонов. Но универсализация не предполагает дублирование кода. При хорошо спроектированном языке и хорошем компиляторе можно генерировать единый код компонентов родового класса, так что последующие добавления потребуют минимальных затрат:

*времени компиляции;

*размера сгенерированного кода;

*времени выполнения;

*памяти, требуемой для выполнения.

Работая в такой среде, можно использовать всю мощь универсализации, не опасаясь потери производительности, как на этапе компиляции, так и выполнения.

Обсуждение: что все-таки не сделано

Основные идеи универсализации уже представлены, но как вы могли заметить, на два важных вопроса не даны ответы.

Первое: в наших усилиях гарантирования безопасности типов мы заняли чересчур консервативную позицию. Конечно, некорректно пытаться втолкнуть круг в стек банковских счетов. Трудно вообразить, какому приложению нужен стек, содержащий точки и банковские счета. Но рассмотрим графическое приложение, для которого вполне естественен стек, содержащий круги, прямоугольники, точки. Такая потребность кажется довольно разумной, но пока мы не можем удовлетворить ее. Система типов, определенная до сих пор, отвергнет вызов figure_stack.put(that_point) если тип figure_stack был объявлен как STACK [FIGURE], а that_point

-тип, отличный от FIGURE. Дадим пока имя рассматриваемым структурам и назовем их

полиморфными структурами данных (polymorphic data structure). Вызов, стоящий перед нами

-как поддержать эти структуры без потери преимуществ безопасности типов.

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

называется ограниченной универсализацией (constrained genericity).

Для обеих этих проблем ОО-метод обеспечивает простые и элегантные решения, оба основанные на комбинировании универсализации и наследования.

Ключевые концепции

*Классы могут иметь формальные родовые параметры, представляющие типы.

*Родовые классы служат для описания общих контейнерных структур данных, реализуемых одинаково, независимо от элементов, которые они содержат.

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

*Клиент родового класса должен предоставлять фактические типы для формальных параметров.

*Единственные допустимые операции над сущностью, чей тип является формальным родовым параметром, - это операции, применимые к любому типу. Сущность может быть правой и левой частью оператора присваивания, фактическим аргументом подпрограммы или операндом теста на равенство или неравенство.

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

*Более гибкое и продвинутое использование универсализации - полиморфные структуры данных и ограниченная универсализация - требует введения наследования.

Библиографические замечания

Один из первых языков, поддерживающий универсализацию - LPG [Bert 1983]. Язык Ada сделал эту концепцию широко известной введением механизма родовых пакетов.

Универсализация была также введена в языки формальной спецификации, такие как Z, CLEAR и OBJ-2, на которые были ссылки в лекции 6 по АТД. Родовой механизм, описанный здесь, был построен на основе механизма, представленного в ранней версии Z [Abrial 1980] [Abrial 1980a] и расширенного в [M 1985b].

Если не считать эту книгу, то одним из первых ОО-языков, поддерживающих универсализацию, был DEC's Trellis язык [Schaffert 1986].

Соседние файлы в папке books