Главная arrow Язык программирования Pascal arrow Турбо Паскаль Начальный курс В.В.Фаронов arrow 23.5. Порядок вызова наследуемого метода Турбо Паскаль В.В. Фаронов

23.5. Порядок вызова наследуемого метода Турбо Паскаль В.В. Фаронов

Большая часть объектов Turbo Vision спроектирована в расчете на их дальнейшее перекрытие в прикладных программах. Типичным примером такого рода объектов является TView, метод Draw которого создает на экране пустой прямоугольник и, следовательно, не может отображать никакой полезной информации. Поскольку все видимые элементы порождены от TView, Вам необходимо перекрыть метод Draw в собственном объекте-потомке. Более того, поскольку TView.Draw не делает никакой полезной работы, его не нужно вызывать в перекрытом методе. Однако полностью перекрываемые методы, подобные TView.Draw, скорее исключение из общего правила. Обычно в перекрытом методе вызывается соответствующий метод, наследуемый от родителя, т.к. в нем реализуются некоторые необходимые для потомка действия. В такого рода ситуациях важна последовательность вызова наследуемого метода: вызывать ли его до реализации специфичных действий или после? Ниже приводятся практические рекомендации на этот счет.
23.5.1. Конструктор
Вызывайте наследуемый Метод до реализации дополнительных действий:
Procedure MyObject.Init(.....);
begin
{Вызов наследуемого конструктора Init}
{Реализация дополнительных действий}
end;
Такая последовательность необходима по той простой причине, что вызов наследуемого конструктора приводит к обнулению всех дополнительных полей объекта MyObject. Если, например, Вы используете следующий фрагмент программы:
type
MyObject = object (TWindow)
Value: Word;
Ok : Boolean;
Constructor Init(var Bounds: TRect; ATitle: TTitleStr;
AValue: Word; AOk: Boolean);
end;
Constructor MyObject.Init;
begin
Inherited Init(Bounds, ATitle, wnNoNumber);
Value := 16;
Ok := True;
end;
то дополнительные поля Value и Ok получат нужные значения 16 и True. Однако, если обращение TWindow.Init (Bounds, ATitle, wnNoNumber); поставить после оператора Ok := True, в них будут помещены значения 0 и False. Из этого правила существует одно исключение, связанное с загрузкой коллекции из потока конструктором Load. Дело в том, что в наследуемом методе TCollection.Load реализуется следующий цикл:
Constructor TCollection.Load (var S: TStream);
begin
.....
for I := 0 to Count - 1 do
AtPut(I, GetItem(S));
end;
Если элементами коллекции являются произвольные наборы двоичных данных (не объекты), Вам потребуется перед чтением очередного элемента сначала получить из потока его длину. Следующий пример иллюстрирует сказанное.
type
PDataCollection = ATDataCollection;
TDataCollection = object (TStringCollection)
ItemSize: Word;
Constructor Load(var S: TStream);
Function GetItem(var S: TStream): Pointer; Virtual;
.....
end;
Constructor TDataCollection.Load(var S: TStream);
begin
S.Read(ItemSize, SizeOf(ItemSize));
Inherited Load(S);
end;
Function TDataCollection.GetItem(var S: TStream): Pointer;
var
Item: Pointer;
begin
GetMem(Item, ItemSize);
S.Read(Item^, ItemSize);
GetItem := Item;
end;
В этом примере конструктор Load сначала загружает из потока поле ItemSize, содержащее длину читаемого элемента. Затем вызывается конструктор TCollection.Load, в котором осуществляется вызов GetItem. Новый GetItem использует поле ItemSize, чтобы определить размер читаемых данных, и резервирует нужный буфер в динамической памяти. Разумеется, запись полиморфных коллекций в поток должна происходить в том же порядке, т.е. сначала записывается длина очередного элемента, а уже потом - его данные.
23.5.2. Деструктор
Вызывайте наследуемый метод после реализации дополнительных действий:
Procedure MyObject.Done;
begin
{Реализация дополнительных действий}
{Вызов наследуемого деструктора Done}
end;
Работа деструктора проходит в обратном порядке по отношению к конструктору. Вначале Вы должны освободить всю дополнительно распределенную динамическую память, а уже затем вызвать наследуемый деструктор, чтобы уничтожить весь объект.
23.5.3. Другие методы
Порядок вызова наследуемого метода зависит от конкретного алгоритма. В большинстве случаев наследуемый метод вызывается первым, но могут использоваться и другие последовательности. Особое значение имеет вызов наследуемого обработчика событий HandleEvent. В самом общем виде структура нового обработчика будет такой:
Procedure MyObject.HandleEvent(var Event: TEvent);
begin
{Изменение наследуемых свойств}
{Вызов наследуемого обработчика}
{Добавление новых свойств}
end;
Таким образом, вначале Вы должны запрограммировать те действия, которые изменяют стандартное поведение перекрытого обработчика, затем вызвать его и, наконец, осуществить новую обработку событий. Разумеется, любая из этих трех частей может отсутствовать. Например, стандартный обработчик TDialog.HandleEvent лишь расширяет свойства наследуемого метода TWindow.HandleEvent, добавляя в него обработку событий от клавиатуры и событий-команд:
Procedure TDialog.HandleEvent(var Event: TEvent);
begin
Inherited HandleEvent(Event);
case Event.what of
evKeyDown:
.....
evCommand:
.....
end ;
end;
Этот обработчик перехватывает все события от клавиатуры и мыши, в том числе и нажатие на клавишу Tab. Если Вы хотите обработать событие от клавиши Tab особым способом, Вы должны перехватить это событие до вызова стандартного обработчика. Например:
Procedure TNoTabsDialog.HandleEvent(var Event: TEvent);
begin
if (Event.What = evKeyDown) then
if (Event.KeyCode = kbTab) or
(Event.KeyCode = kbShiftTab) then
ClearEvent(Event); Inherited HandleEvent(Event);
end;