currentLink = [ currentLink nextElement ];
}
@end
Стандартной альтернативой является создание вспомогательного объекта, называемого
итератором (iterator). Этот объект поставляется разработчиком контейнерного класса
(например, связного списка). Единственное назначение такого объекта — обеспечить
доступ к элементам контейнера (один элемент за одно обращение) без показа внутренней
структуры списка. Обычно итератор содержит указатель, с которым производятся
всевозможные манипуляции. Листинг 15.1 иллюстрирует, как можно определить итератор
для абстракции связного списка.
Итератор обычно определяется самим списком как реакция на сообщение. Поэлементный
цикл производится следующим образом:
id aList; /* объявление списка */
id itr; /* объявление итератора */
for (itr = [ aList iterator ]; ! [ itr atEnd ];
[itr advance ])
print( [ itr value ] );
Заметьте, что хотя цикл потребовал объявления дополнительной переменной-итератора,
ее использование не подразумевает знание внутренней структуры связного списка.
Легкость, с которой конструируются и обрабатываются абстракции данных, — это один
из основных рекламных лозунгов языков с динамическими типами. Контейнеры имеют
совершенно общий вид и могут даже содержать разнородные наборы данных различных
типов. К сожалению, такой выигрыш дается недаром. Как мы отметили ранее, существует
дилемма между легкостью в использовании и эффективностью выполнения. Программы
на динамических языках редко выполняются столь же эффективно, как в языках с более
строгим контролем типов.
15.3. Контейнеры в языках со строгим контролем типа данных
Мы переходим теперь к рассмотрению того, как контейнерные классы конструируются в
языках со строгим контролем типа данных (Object Pascal, C++). Есть мнение, что принцип
подстановки сам по себе обеспечивает решение проблемы контейнерных классов в таких
языках. Согласно главе 6 принцип подстановки утверждает, что переменной, которая
объявлена с определенным типом, можно на самом деле присвоить значение подтипа.
Принцип подстановки на самом деле до некоторой степени облегчает решение наших
проблем, но далеко не всех.
Чтобы использовать подстановку, мы прежде всего должны создать класс, который бы
был родителем всего, что мы захотим хранить в нашей структуре данных. Назовем этот
гипотетический класс ListElement. Затем создадим абстракцию списков с элементами.
Листинг 15.2 показывает, как это можно сделать в Object Pascal.
Объекты, хранящиеся в списке, должны быть описаны как подклассы класса ListElement.
Соответственно мы не можем построить список с целыми или вещественными числами,
пока не породим эти типы данных из класса ListElement. Обычно это не очень серьезная
проблема. На самом деле мы получили разнородный список со значениями различного
типа, если только все они являются подклассами базового класса ListElement.
PDF created with pdfFactory Pro trial version www.pdffactory.com