type
TField = class
functionGetData: string; virtual; abstract;
end;
TStringField = class(TField)
Data: string;
functionGetData: string; override;
end;
TIntegerField = class(TField)
Data: Integer;
functionGetData: string; override;
end;
TExtendedField = class(TField)
Data: Integer;
function GetData: string; override;
end;
functionTStringField.GetData;
begin
GetData:= Data;
end;
functionTIntegerField.GetData;
begin
GetData:= IntToStr(Data);
end;
functionTExtendedField.GetData;
begin
GetData:= IntToStrF(Data, ffFixed, 7, 2);
end;
…
procedureShowData(AField: TField);
begin
Form1.Label1.Caption:= AField.GetData;
end;
В этом примере классы содержат разнотипные данные, которые "умеют" только сообщить о значении этих данных текстовой строкой (при помощи метода GetData). Внешняя по отношению к ним процедура ShowData получает объект в виде параметра и показывает эту строку.
Правила контроля соответствия типов (typecasting) языка Pascal говорят о том, что объекту как указателю на экземпляр объектного типа может быть присвоен адрес любого экземпляра любого из дочерних типов. В процедуре ShowData параметр описан как TField. Это значит, что в нее можно передавать объекты классов и TStringField, и TIntegerField, и TExtendedField, и любого другого потомка TField.
Но какой (точнее, чей) метод GetData при этом будет вызван? Тот, который соответствует классу фактически переданного объекта. Этот принцип называется полиморфизмом, и он, пожалуй, представляет собой наиболее важный "козырь" ООП. Допустим, имеется дело с некоторой совокупностью явлений или процессов. Чтобы смоделировать их средствами ООП, нужно выделить их самые общие, типовые черты. Те из них, которые не изменяют своего содержания, должны быть реализованы в виде статических методов. Те же, которые варьируют при переходе от общего к частному, лучше облечь в форму виртуальных методов. Основные "родовые" черты (методы) нужно описать в классе-предке и затем перекрыть их в классах-потомках. В предыдущем примере программисту, пишущему процедуру вроде ShowData, важно лишь одно: то, что любой объект, переданный в нее, является потомком TField, и он умеет сообщить о значении своих данных (выполнив метод GetData).
Если такую процедуру скомпилировать и поместить в динамическую библиотеку, то эту библиотеку можно будет раз и навсегда использовать без изменений, хотя будут появляться и новые, неизвестные в момент ее создания классы-потомки TField!
Наглядный пример использования полиморфизма дает сама Delphi. В ней имеется класс TComponent, на уровне которого сосредоточены определенные "правила" того, как взаимодействовать со средой разработки и с другими компонентами. Следуя этим правилам, можно порождать от TComponent свои компоненты, настраивая Delphi на решение специальных задач.
8. ОБРАБОТКА СООБЩЕНИЙ
Потребность в динамических методах особенно ощутима при разработке объектов, соответствующих элементам интерфейса Windows, когда каждый из большой иерархии объектов содержит обработчики десятков разнообразных сообщений.
Методы, предназначенные специально дня обработки сообщений Windows, составляют подмножество динамических методов и объявляются директивой message, за которой следует индекс — идентификатор сообщения. Они должны быть обязательно описаны как процедуры, имеющие один var-параметр, который может быть описан произвольно, например:
type
TMyControl = class(TWinControl)
procedureWMSize( varMessage: TWMSize);
messageWM_SIZE;
end;
type
TMyOtherControl = class(TMyControl)
procedureResize( varInfo);
messageWM_SIZE;
end;
Для перекрытия методов-обработчиков сообщений директива overrideне используется. В этом случае нужно сохранить в описании директиву messageс индексом метода.
Необходимости изобретать собственные структуры, по-своему интерпретирующие содержание того или иного сообщения, нет: для большинства сообщений Windows типы уже описаны в модуле MESSAGES.
В обработчиках сообщений (и только в них) можно вызвать метод-предок, просто указав ключевое слово inherited, без указания его имени и преобразования типа параметров: предок будет найден по индексу. Следует напомнить, что система обработки сообщений встроена в Object Pascal на уровне модели объектов, и самый общий обработчик — метод DefaultHandler — описан в классе TObject.
Читать дальше