Литмир - Электронная Библиотека
Содержание  
A
A

Для перекрытия и виртуальных, и динамических методов служит новая директива override, с помощью которой (и только с ней!) можно переопределять оба этих типа методов:

type

TFirstClass = <b>class</b>

FMyField1: Integer;

FMyField2: Longlnt;

<b>procedure </b>StatMethod1;

<b>procedure </b>VirtMethod1; <b>virtual;</b>

<b>procedure </b>VirtMethod2; <b>virtual;</b>

<b>procedure </b>DynaMethod1; <b>dynamic;</b>

<b>procedure </b>DynaMethod2; <b>dynamic;</b>

<b>end;</b>

TSecondClass = <b>class</b>(TFirstClass)

<b>procedure </b>StatMethod;

<b>procedure </b>VirtMethod; <b>override;</b>

<b>procedure </b>StatMethod; <b>override;</b>

<b>end;</b>

<b>var</b>

Obj2: TFirstClass;

Obj1: TSecondClass;

Первый из методов в примере создается заново, остальные два — перекрываются. Попытка применить override к статическому методу вызовет ошибку компиляции.

В Object Pascal абстрактными называются методы, которые определены в классе, но не содержат никаких действий, никогда не вызываются и должны быть переопределены в потомках класса. Абстрактными могут быть только виртуальные и динамические методы. Для этого используется директива abstract, указываемая при описании метода:

<b>procedure </b>NeverCallMe; <b>virtual; abstract;</b>

При этом никакого кода для этого метода писать не нужно. Как и ранее, вызов метода NeverCallMe приведет к ошибке времени выполнения.

7. ПОЛИМОРФИЗМ

Рассмотрим пример, которой поясняет, для чего нужно использование абстрактных методов. Пусть у нас имеются некое обобщенное поле для хранения данных — класс TField и три его потомка — для хранения строк, целых и вещественных чисел. В данном случае класс Tfield не используется сам по себе; его основное предназначение — быть родоначальником иерархии конкретных классов — "полей" и дать возможность абстрагироваться от частностей. Хотя параметр у ShowData и описан как TField, но если поместить туда объект этого класса, произойдет ошибка вызова абстрактного метода:

<b>type</b>

TField = <b>class</b>

<b>function </b>GetData: <b>string; virtual; abstract;</b>

<b>end;</b>

TStringField = <b>class</b>(TField)

Data: <b>string;</b>

<b>function </b>GetData: <b>string; override;</b>

<b>end;</b>

TIntegerField = <b>class</b>(TField)

Data: Integer;

<b>function </b>GetData: <b>string; override;</b>

<b>end;</b>

TExtendedField = <b>class</b>(TField)

Data: Integer;

function GetData: <b>string; override;</b>

<b>end;</b>

<b>function </b>TStringField.GetData;

<b>begin</b>

GetData:= Data;

<b>end;</b>

<b>function </b>TIntegerField.GetData;

<b>begin</b>

GetData:= IntToStr(Data);

<b>end;</b>

<b>function </b>TExtendedField.GetData;

<b>begin</b>

GetData:= IntToStrF(Data, ffFixed, 7, 2);

<b>end;</b>

<b>…</b>

<b>procedure </b>ShowData(AField: TField);

<b>begin</b>

Form1.Label1.Caption:= AField.GetData;

<b>end;</b>

В этом примере классы содержат разнотипные данные, которые "умеют" только сообщить о значении этих данных текстовой строкой (при помощи метода GetData). Внешняя по отношению к ним процедура ShowData получает объект в виде параметра и показывает эту строку.

Правила контроля соответствия типов (typecasting) языка Pascal говорят о том, что объекту как указателю на экземпляр объектного типа может быть присвоен адрес любого экземпляра любого из дочерних типов. В процедуре ShowData параметр описан как TField. Это значит, что в нее можно передавать объекты классов и TStringField, и TIntegerField, и TExtendedField, и любого другого потомка TField.

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

Если такую процедуру скомпилировать и поместить в динамическую библиотеку, то эту библиотеку можно будет раз и навсегда использовать без изменений, хотя будут появляться и новые, неизвестные в момент ее создания классы-потомки TField!

Наглядный пример использования полиморфизма дает сама Delphi. В ней имеется класс TComponent, на уровне которого сосредоточены определенные "правила" того, как взаимодействовать со средой разработки и с другими компонентами. Следуя этим правилам, можно порождать от TComponent свои компоненты, настраивая Delphi на решение специальных задач.

110
{"b":"943277","o":1}