Вызов метода, который переопределяется, не является лучшим стилем. В общем случае возможно, что TEmployee.Init выполняет важную, однако скрытую инициализацию.
Вызывая переопределяемый метод, необходимо быть уверенным в том, что порожденный тип объекта включает функциональность родителя. Кроме того, любое изменение в родительском методе автоматически оказывает влияние на все порожденные.
После вызова TEmployee.Init, THourly.Init может затем выполнить свою собственную инициализацию, которая в этом случае состоит только в присвоении значения, переданного в ATime.
Другим примером переопределяемого метода является функция THourly.GetPayAmount, вычисляющая сумму выплат работающему на почасовой ставке. В действительности, каждый тип объекта TEmployee имеет свой метод GetPayAmount, так как тип работающего зависит от того, как производится расчет. Метод THourly.GetPayAmount должен учитывать, сколько часов работал сотрудник, были ли сверхурочные работы, каков коэффициент увеличения за сверхурочные работы и т. д.
Метод TSalaried. GetPayAmount должен лишь делить ставку работающего на число выплат в каждом году (в нашем примере).
unit Workers;
interface
const
PayPeriods = 26; {в год}
OvertimeThreshold = 80; {за каждый период оплаты}
OvertimeFactor =1.5; {увеличение против обычной оплаты}
type
TEmployee = object
Name, Title: string[25];
Rate: Real;
procedure Init (AName, ATitle: string; ARate: Real);
function GetName : String;
function GetTitle : String;
function GetRate : Real;
function GetPayAmount : Real;
end;
THourly = object(TEmployee)
Time: Integer;
procedure Init(AName, ATitle: string; ARate:
Real, Atime: Integer);
function GetPayAmount : Real;
function GetTime : Real;
end;
TSalaried = object(TEmployee)
function GetPayAmount : Real;
end;
TCommissioned = object(TSalaried)
Commission : Real;
SalesAmount : Real;
constructor Init (AName, ATitle: String; ARate,
ACommission, ASalesAmount: Real);
function GetPayAmount : Real;
end;
implementation
function RoundPay(Wages: Real) : Real;
{округляем сумму выплат, чтобы игнорировать суммы меньше
денежной единицы}
begin
RoundPay := Trunc(Wages * 100) / 100;
.
.
.
TEmployee является вершиной нашей иерархии объектов и содержит первый метод GetPayAmount.
function TEmployee.GetPayAmount : Real;
begin
RunError(211); { дать ошибку этапа выполнения }
end;
Может вызвать удивление тот факт, что метод дает ошибку времени выполнения. Если вызывается Employee.GetPayAmount, то в программе возникает ошибка. Почему? Потому что TEmployee является вершиной нашей иерархии объектов и не определяет реального рабочего; следовательно, ни один из методов TEmployee не вызывается определенным образом, хотя они и могут быть наследованными. Все наши работники являются либо почасовиками, либо имеют оклады, либо работают на сдельщине. Ошибка на этапе выполнения прекращает выполнение программы и выводит 211, что соответствует сообщению об ошибке, связанной с вызовом абстрактного метода (если программа по ошибке вызывает TEmployee.GetPayAmount).
Ниже приводится метод THourly.GetPayAmount, в котором учитываются такие вещи, как сверхурочная оплата, число отработанных часов и т. д.
function THourly.GetPayAMount : Real;
var
OverTime: Integer;
begin
Overtime := Time – OvertimeThreshold;
if Overtime > 0 then
GetPayAmount := RoundPay(OvertimeThreshold * Rate +
Rate OverTime * OvertimeFactor * Rate)
else
GetPayAmount := RoundPay(Time * Rate)
end;
Метод TSalaried.GetPayAmount намного проще; в нем ставка
делится на число выплат:
function TSalaried.GetPayAmount : Real;
begin
GetPayAmount := RoundPay(Rate / PayPeriods);
end;
Если взглянуть на метод TCommissioned.GetPayAmount, то будет видно, что он вызывает TSalaried.GetPayAmount, вычисляет комиссионные и прибавляет их к величине, возвращаемой методом TSalaried. GetPayAmount.
function TСommissioned.GetPayAmount : Real;
begin
GetPayAmount := RoundPay(TSalaried.GetPayAmount +
Commission * SalesAmount);
end;
Важное замечание: хотя методы могут быть переопределены, поля данных переопределяться не могут. После того как было определено поле данных в иерархии объекта, никакой дочерний тип не может определить поле данных в точности с таким же именем.
3. Совместимость типов объектов
Наследование до некоторой степени изменяет правила совместимости типов в Borland Pascal. Помимо всего прочего, порожденный тип наследует совместимость типов всех своих порождающих типов.
Читать дальше
Конец ознакомительного отрывка
Купить книгу