Файл: 10. Разработка универсальных полиморфных контейнеров.pdf

Добавлен: 20.10.2018

Просмотров: 959

Скачиваний: 9

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

 

 

10. Разработка универсальных полиморфных контейнеров 

Механизм  переопределения  методов  и  свойство  полиморфности 

объектных  переменных  позволяют  создавать  универсальные  объекты-

контейнеры, которые могут хранить и обрабатывать объекты разных классов, 

входящих в некоторую общую иерархию. При необходимости можно создать 

сверхуниверсальный  контейнер,  способный  хранить  объекты  абсолютно 

любых  классов.  Для  этого,  как  уже  указывалось  в  разделе  9,  достаточно  в 

качестве базового типа использовать корневой класс  Object/ TObject. Однако 

на  практике  вместо  такого  универсального  хранилища  часто  удобнее 

использовать  более  специализированные  контейнеры  для  некоторой 

подиерархии классов, которые по смыслу близки друг другу. 

В  качестве  первого  примера  такого  специализированного  хранилища 

рассмотрим  контейнер  для  графических  объектов.  Ранее  в  разделах  4  и  6 

были  рассмотрены  два  простейших  варианта  контейнера  для  хранения  и 

обработки  объектов-окружностей.  Теперь,  после  изучения  принципов 

наследования и полиморфизма, эти простейшие контейнеры можно сделать 

более  универсальными,  способными  хранить  и  обрабатывать  любые 

графические объекты. 

Обязательным  условием  реализации  такого  контейнера  является 

наличие  библиотеки  графических  классов,  удовлетворяющей  следующим 

требованиям: 

  в  вершине  иерархии  находится  корневой  класс  (например  –  класс 

Figure) 

  в  корневом  классе  объявлен  виртуальный  абстрактный  метод 

отображения фигуры (например – Show) 

  в  корневом  классе  объявлен  и  реализован  обычный  метод 

перемещения, который наследуется в каждом дочернем классе 

  в каждом дочернем классе переопределен метод отображения 

 


background image

 

 

Рассмотрим  сначала  реализацию  контейнера  для  графических  объектов 

на  основе  обычного  массива.  Превратить  контейнер  для  окружностей  в 

универсальный  достаточно  просто  –  надо  лишь  везде  заменить  классовый 

тип 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


background image

 

 

 

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){ // код получения имени объекта } 

//  конец описания класса

 

 

Некоторые комментарии к этим классам. 

  объявление  базового  массива  основано  на  свойстве  полиморфности 

объектных  переменных:  элементы  массива  могут  хранить  адреса 

размещения в памяти любых графических объектов 

  в  методе  добавления  используется  возможность  объявления 

полиморфного параметра, через который передается адрес конкретного 

добавляемого в контейнер объекта 

  в  методе  удаления  тип  возвращаемого  значения  также  является 

полиморфным: можно получить ссылку на любой удаляемый объект 

  в  новых  методах  выборочного  отображения  и  перемещения  через 

входной  параметр  передается  имя  класса,  который  является 


background image

 

 

родоначальником  той  подиерархии,  объекты  которой  надо  показать 

или  переместить;  само  отображение  или  перемещение  реализуется  с 

использованием  операторов  проверки  динамического  типа  объектов, 

т.е. операторов   is  или  instanceof 

  новый метод GetObjectName  для получения имени того класса, объект 

которого  связан  в  данный  момент  с  заданным  элементом  массива, 

должен  использовать  средства,  предоставляемые  механизмом 

рефлексии 

Что  касается  использования  универсального  контейнера,  то  оно 

включает выполнение обычных действий: 

  объявление объектной переменной контейнерного типа 

  создание контейнера с помощью конструктора 

  создание 

необходимых 

графических 

объектов 

с 

помощью 

соответствующих конструкторов 

  добавление созданных объектов в контейнер 

  полное  или  частичное  отображение  находящихся  в  контейнере 

объектов 

  полное или частичное перемещение объектов 

  удаление заданных объектов 

 

Альтернативная  реализация  –  на  основе  динамического  списка 

полиморфных указателей.

 

 

 

 

 

 

 

  

 

элемент 1 

следующий 

элемент 

адрес 

объекта 

 

элемент 2 

следующий 

элемент 

адрес 

объекта 

 

элемент 3 

следующий 

элемент 

адрес 

объекта 

 

последний 

элемент  

nil = null = 0 

адрес объекта 

 

объект -

окружность 

объект- 
отрезок 

объект -

прямоугольник 

объект -

окружность 


background image

 

 

В разделе 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 ( ) {…;}