Файл: 10. Разработка универсальных полиморфных контейнеров.pdf
ВУЗ: Университет управления «ТИСБИ»
Категория: Учебное пособие
Дисциплина: Объектно-ориентированное программирование
Добавлен: 20.10.2018
Просмотров: 1058
Скачиваний: 9
10. Разработка универсальных полиморфных контейнеров
Механизм переопределения методов и свойство полиморфности
объектных переменных позволяют создавать универсальные объекты-
контейнеры, которые могут хранить и обрабатывать объекты разных классов,
входящих в некоторую общую иерархию. При необходимости можно создать
сверхуниверсальный контейнер, способный хранить объекты абсолютно
любых классов. Для этого, как уже указывалось в разделе 9, достаточно в
качестве базового типа использовать корневой класс Object/ TObject. Однако
на практике вместо такого универсального хранилища часто удобнее
использовать более специализированные контейнеры для некоторой
подиерархии классов, которые по смыслу близки друг другу.
В качестве первого примера такого специализированного хранилища
рассмотрим контейнер для графических объектов. Ранее в разделах 4 и 6
были рассмотрены два простейших варианта контейнера для хранения и
обработки объектов-окружностей. Теперь, после изучения принципов
наследования и полиморфизма, эти простейшие контейнеры можно сделать
более универсальными, способными хранить и обрабатывать любые
графические объекты.
Обязательным условием реализации такого контейнера является
наличие библиотеки графических классов, удовлетворяющей следующим
требованиям:
в вершине иерархии находится корневой класс (например – класс
Figure)
в корневом классе объявлен виртуальный абстрактный метод
отображения фигуры (например – Show)
в корневом классе объявлен и реализован обычный метод
перемещения, который наследуется в каждом дочернем классе
в каждом дочернем классе переопределен метод отображения
Рассмотрим сначала реализацию контейнера для графических объектов
на основе обычного массива. Превратить контейнер для окружностей в
универсальный достаточно просто – надо лишь везде заменить классовый
тип Circle общеродовым классовым типом Figure. Однако универсальный
характер контейнера позволяет расширить базовую функциональность
простейшего контейнера для окружностей за счет ряда новых методов, таких
как:
выборочное отображение объектов
выборочное перемещение объектов
получение имени объекта, связанного с заданным элементом массива
Вот описание класса для универсального контейнера, как обычно – на
двух языках (для наглядности те изменения, которые появились по
сравнению с начальным вариантом, выделены курсивом и подчеркнуты):
TArrayFigsContainer = class
private
count : integer; // текущее число объектов в контейнере
Figs : array [1..100] of TFigure; // массив указателей на объекты
public
constructor Create; // создаем пустой контейнер
function GetCount : integer;
function Add (aFig : TFigure) : boolean; // добавляем в конец набора
function Delete (ai : integer) : TFigure; // удаление объекта по номеру
function SearchName (aName : string):integer; // поиск по имени объекта
procedure ShowAll; // метод-итератор для показа всех объектов
procedure ShowOnly (aName : string); // выборочное отображение
procedure MoveAll (dx, dy : integer); // общее перемещение
procedure MoveOnly (dx, dy : integer; aName : string); // выборочное
function GetObjectName (ai : integer): string; // получение имени объекта
end;
class ArrayFigsContainer {
private Figure [ ] Figs; // будущий массив указателей
private int Count; // счетчик числа объектов в контейнере
public ArrayFigsContainer (int aSize) {
Figs = new Figure [aSize]; // создание массива
Count = 0; }
public int GetCount { return Count;}
public int GetSize { return Figs.Length; }
public bool Add (Figure aFig) { // код добавления нового объекта }
public Figure Delete (int ai) { // код удаления объекта по его номеру}
public int SearchName (string aName) { // код поиска по имени }
public void ShowAll( ) { // код отображения всех объектов }
public void ShowOnly(string aName) {//код выборочного отображения }
public void MoveAll (int dx, int dy) { // код перемещения }
public void MoveOnly (int dx, int dy, string aName ) { // код выборочного }
public string GetObjectName (int ai){ // код получения имени объекта }
}
// конец описания класса
Некоторые комментарии к этим классам.
объявление базового массива основано на свойстве полиморфности
объектных переменных: элементы массива могут хранить адреса
размещения в памяти любых графических объектов
в методе добавления используется возможность объявления
полиморфного параметра, через который передается адрес конкретного
добавляемого в контейнер объекта
в методе удаления тип возвращаемого значения также является
полиморфным: можно получить ссылку на любой удаляемый объект
в новых методах выборочного отображения и перемещения через
входной параметр передается имя класса, который является
родоначальником той подиерархии, объекты которой надо показать
или переместить; само отображение или перемещение реализуется с
использованием операторов проверки динамического типа объектов,
т.е. операторов is или instanceof
новый метод GetObjectName для получения имени того класса, объект
которого связан в данный момент с заданным элементом массива,
должен использовать средства, предоставляемые механизмом
рефлексии
Что касается использования универсального контейнера, то оно
включает выполнение обычных действий:
объявление объектной переменной контейнерного типа
создание контейнера с помощью конструктора
создание
необходимых
графических
объектов
с
помощью
соответствующих конструкторов
добавление созданных объектов в контейнер
полное или частичное отображение находящихся в контейнере
объектов
полное или частичное перемещение объектов
удаление заданных объектов
Альтернативная реализация – на основе динамического списка
полиморфных указателей.
элемент 1
следующий
элемент
адрес
объекта
элемент 2
следующий
элемент
адрес
объекта
элемент 3
следующий
элемент
адрес
объекта
последний
элемент
nil = null = 0
адрес объекта
объект -
окружность
объект-
отрезок
объект -
прямоугольник
объект -
окружность
В разделе 6 было отмечено, что для реализации спискового контейнера в
общем случае надо использовать два класса – класс элементов списка и
класс самого спискового контейнера. Для перехода от простого контейнера
к универсальному необходимо заменить конкретный классовый тип Circle на
общий тип Figure.
Описание измененных классов для элементов спискового контейнера:
ListItem = class
private
ItemInf : string; // или другой необходимый тип
Next : ListItem; // свойство-указатель на следующий элемент
Fig : TFigure; // свойство-указатель адресуемого объекта
public
сonstructor Create (aInf : string; aNext : ListItem; aFig: TFigure);
function GetNext : ListItem; // получение адреса след. элемента
function GetFig : TFigure; // важно: получение адреса объекта!
function GetInf : string;
procedure SetNext (aNext : ListItem); // изменение адреса след. эл-та
procedure SetFig (aFig : TFigure); // для присоединения другого объекта
procedure SetInf (aInf : string); // для изменения информационного поля
end;
class ListItem {
private int ItemInf; // или другой необходимый тип
private ListItem Next; // свойство-указатель на следующий элемент
private Figure Fig; // свойство-указатель адресуемого объекта
public ListItem (int aInf, ListItem aNext, Figure aFig) {…;}
public ListItem GetNext( ) {…;} // получение адреса след. элемента
public Figure GetFig( ) {…;} // важно: получение адреса объекта!
public int GetInf ( ) {…;}