Полиформные коллекции
В объектах TCircle (окружность) и TRectangle (прямоугольник) инкапсулированы новые поля, поэтому перекрываются также и методы Init:
Constructor TCircle.Init; {Создает окружность случайного радиуса в случайном месте} begin TGraphObject.Init; {Получаем координаты центра} R: = Random(GetMaxY div 2) {Получаем радиус} end; Procedure TCircle.Draw; {Вычерчивает окружность} begin Circle(X, Y, R) end; Constructor TRectangle.init; {Создает случайный прямоугольник} begin TGraphObject.Init;{Верхний левый угол} W: = Random(GetMaxX div 2) {Ширина} H: = Random(GetMaxY div 2) {Высота} end; Procedure TRectangle.Draw; {Вычерчивает прямоугольник} begin Rectangle(X, Y, X+W, Y+H) end;
После того как определены нужные объекты, не составляет особого труда поместить эти объекты в коллекцию и вывести их на экран. Например, для вывода всех элементов коллекции можно использовать такую процедуру:
Procedure DrawAll (C: PCollection); {Выводит все элементы полиморфной коллекции} Procedure DrawItem(p: PGraphObject); far; begin p.Draw {Это и есть полиморфизм в действии!} end; begin С.ForEach(@DrawItem) end;
Как видим, в процедуре DrawItem полиморфизм используется дважды: во-первых, метод ForEach обращается к ней, передавая в качестве параметра обращения нетипизированный указатель на элемент коллекции; это позволяет трактовать параметр как указатель на любой объект, в том числе и на TGraphObject. Во-вторых, в процедуре используется обращение к виртуальному методу объекта-родителя Draw: поскольку этот метод перекрывается во всех потомках, каждый из них будет использовать свой метод Draw для вывода на экран.
Сформируем программу, поместив в нее вместо, точек уже рассмотренные фрагменты:
Uses Objects,Graph,CRT; type ….. Constructor TGraphObject.Init; ….. Procedure TGraphObject.Draw; ….. Constructor TPoint.Init; ….. Procedure TPoint.Draw; ….. Constructor TCircle.Init; ….. Procedure TCircle.Draw; ….. Constructor TRectangle.Init; ….. Procedure TRectangle.Draw; ….. Procedure DrawAll(C: PCollection); ….. var a, r, k: Integer; List: PCollection; p: Pointer; begin .a: = 0; {Инициируем графический режим работы экрана:} InitGraph(a, r, '\TP\BGI'); r: = GraphResult; if r < > 0 then WriteLn(GraphErrorMsg(r)) {Ошибка инициации} else begin {Создаем коллекцию:} List: = New(PCollection, Init (20.5)); {Наполняем ее 20 элементами:} for k: = 1 to 20 do begin case k mod 3 of 0: p: = New(PPoint, Init); 1: p: = New(PCircle, Init); 2: p: = New(PRectangle, Init) end; if p < > NIL then List.Insert(p) end; DrawAll(List); {Выводим на экран все элементы} While not KeyPressed do;{Ждем нажатия на любую клавишу} CloseGraph {Возвращаемся в текстовый режим} end end.
В этой программе предполагается, что драйвер графического экрана расположен в каталоге \TF\BGI на текущем диске. Если это не так, следует указать маршрут поиска этого драйвера в качестве параметра обращения к процедуре InitGraph. Кроме того, каталог, содержащий стандартную графическую библиотеку Graph, должен быть указан опцией Options/Directories/Unit directories, если, разумеется, библиотека не содержится в текущем каталоге.