Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ООП.doc
Скачиваний:
23
Добавлен:
08.11.2018
Размер:
1.4 Mб
Скачать

Перекрытие методов доступа к свойствам

Рассмотрим далее различные варианты перекрытия методов доступа к свойствам. Статическое перекрытие методов доступа к свойству в потомках не влияет на сами свойства. Для чтения и записи свойства всегда будет использоваться методы, объявленные в том же классе, что и само свойство, независимо от типа указателя или типа реального объекта. Например:

interface Type TSomeOb1 = class private ... protected function Method1: integer; procedure Method2(data: integer); public property Value: integer read Method1 write Method2; end;

TSomeOb2 = class(TSomeOb1) private ... protected function Method1: integer; procedure Method2(data: integer); end;

Implementation procedure Test; var MyObject1: TSomeOb1; MyObject2: TSomeOb2; X: integer; begin MyObject1 := TSomeOb2.Create; MyObject2 := TSomeOb2.Create; ... MyObject1.Value := 10; //будет вызван метод TSomeOb1.Method2

X := MyObject1.Value; //будет вызван метод TSomeOb1.Method1

MyObject2.Value := 10; //будет вызван метод TSomeOb1.Method2

X := MyObject2.Value; //будет вызван метод TSomeOb1.Method1 ... MyObject1.Free; MyObject2.Free; end;

В этом примере при чтении или записи свойства Value как объекта MyObject1, так и MyObject2 будут вызваны методы Method1 и Method2, определенные в классе TSomeOb1, поскольку именно в этом классе определено свойство.

Если в качестве методов чтения и записи свойств использовать виртуальные или динамические методы, то вызываться будет метод, соответствующий реальному классу объекта:

interface Type TSomeOb1 = class protected a: real; procedure SetA(x: real); virtual; function GetA: real; virtual; public property PropA: real read GetA write setA; ... end;

TSomeOb2 = class(TSomeOb1) protected a: real; procedure SetA(x: real); override; function GetA: real; override; public ... end;

Implementation procedure Test; var Ob: TSomeOb1; x: real; begin Ob := TSomeOb2.Create; Ob.PropA := 2; //будет вызван метод TSomeOb2.SetA в соответствии с реальным //типом объекта т.к. он виртуальный x := Ob.PropA; //будет вызван метод TSomeOb1.GetA в соответствии с реальным //типом объекта т.к. он виртуальный ... Ob.Destroy; end;

Сравним действие статического и виртуального перекрытия методов доступа к свойствам:

interface Type TSomeOb1 = class protected a: real; procedure SetA(x: real); virtual; function GetA: real; public property PropA: real read GetA write setA; end;

TSomeOb2 = class(TSomeOb1) protected //метод SetA перекрывается как виртуальный procedure SetA(x: real); override; //метод GetA перекрывается как статический function GetA: real; end;

TSomeOb3 = class(TSomeOb2) protected a: real; //метод SetA перекрывается как статический procedure SetA(x: real); reintroduce //метод GetA перекрывается как статический function GetA: real; public //перекрывается свойство property PropA read GetA write setA; end;

Implementation procedure Test; var Ob: TSomeOb1; Ob3: TSomeOb3; x: real; begin Ob := TSomeOb2.Create; Ob.PropA := 2; //будет вызван метод TSomeOb2.SetA в соответствии //с реальным типом объекта т.к. он виртуальный x := Ob.PropA; //будет вызван метод TSomeOb1.GetA в соответствии с типом //указателя т.к. он статический Ob.Destroy;

Ob := TSomeOb3.Create; Ob.PropA := 2; //будет вызван метод TSomeOb2.SetA т.к. этот метод //виртуальный, а класс TSomeOb2 наиболее близкий предок //реального класса объекта TSomeOb3, где этот метод //перекрыт x := Ob.PropA; //будет вызван метод TSomeOb1.GetA в соответствии с типом //указателя т.к. он статический и перекрытие свойств //также является статической операцией Ob.Destroy;

Ob3 := TSomeOb3.Create; Ob3.PropA := 2; x := Ob3.PropA; //в обоих случаях будут вызваны методы класса TSomeOb3 //т.к. это и тип указателя и тип реального объекта, //а свойство в классе TSomeOb3 перекрывается Ob3.Destroy; end;

В этом примере сначала создается объект класса TSomeOb2, причем переменная имеет тип TSomeOb1. В классе TSomeOb2 свойство не переопределяется и не перекрывается. Поэтому методы чтения и записи вызываются в соответствии с общими правилами вызова статических и виртуальных или динамических методов. Во втором случае указатель по-прежнему имеет тип TSomeOb1, а реальный тип объекта TSomeOb3. В классе TSomeOb3 свойство перекрывается, но операция перекрытия свойства по своей природе статическая, то есть берется то свойство, которое было объявлено в классе TSomeOb1. И далее по-прежнему действуют правила вызова статических и виртуальных или динамических методов. Обратите внимание – не будет вызван метод TSomeOb3.SetA, поскольку он перекрывает виртуальный метод TSomeOb2.GetA как статический. В третьем случае и тип указателя, и тип реально созданного объекта TSomeOb3, в котором перекрыто свойство. Выполняется вызов методов доступа к свойству, определенных в классе TSomeOb3.

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

Для демонстрации использования виртуальных методов доступа к свойствам вернемся к одному из старых примеров – иерархии классов информационной системы для хранения и обработки данных обо всех людях, связанных с каким-либо учебным заведением. Заменим в ней метод, возвращающий в виде одной строки различные для разных категорий лиц общие сведения, на свойство:

Interface type TPerson = class(TObject) //организуем свойства фамилия и номер паспорта private ... protected //функция, возвращающая все данные в виде одной строки function GetStringData: string; virtual; public property Name: string read FName write SetName; property PassportNumber: string read FPassportNumber write SetPassportNumber; //свойство, возвращающее все данные в виде одной строки property FullData: string read GetStringData; ... end;

TStudent = class(TPerson) //организуем свойства номер зачетки, группа, специальность //свойства фамилия и номер паспорта унаследованы private ... protected //функция, возвращающая все данные в виде одной строки function GetStringData: string; override; public property TestBookNumber: string read FTestBookNumber write SetTestBookNumber; property Group: string read FGroup write SetGroup; property Subject: string read FSubject write SetSubject; //свойство FullData не перекрывается ... end;

TWorker = class(TPerson) //организуем свойство табельный номер //свойства фамилия и номер паспорта унаследованы private ... protected //функция, возвращающая все данные в виде одной строки function GetStringData: string; override; public property TableNumber: string read FTableNumber write SetTableNumber; //свойство FullData не перекрывается ... end;

TEmployee = class(TWorker) //организуем свойство должность //свойства фамилия, номер паспорта и табельный номер //унаследованы private ... protected //функция, возвращающая все данные в виде одной строки function GetStringData: string; override; public property Appointment: string read FAppointment write SetAppointment; //свойство FullData не перекрывается ... end;

TTeacher = class(TWorker) //организуем свойства кафедра и преподаваемые дисциплины //свойства фамилия, номер паспорта и табельный номер //унаследованы private ... protected //функция, возвращающая все данные в виде одной строки function GetStringData: string; override; public property Chair: string read Fchair write SetChair; property Subjects: TStrings read FSubjects write SetSubjects; //свойство FullData не перекрывается ... end;

var Persons: array[1..MaxPersonNum] of TPerson;

implementation

//функция, возвращающая все данные в виде одной строки function TPerson.GetStringData: string; begin result := Name + ‘ ‘ + PassportNumber; end;

function TStudent.GetStringData: string; begin result := inherited GetStringData + ‘ ‘ + TestBookNumber + + Group + ‘ ‘ + Subject; //inherited GetStringData – вызовется метод предка, //который вернет фамилию + номер паспорта end;

function TWorker.GetStringData: string; begin result := inherited GetStringData + ‘ ‘ + TableNumber; //inherited GetStringData – вызовется метод предка, // который вернет возвращены фамилию + номер паспорта end;

function TEmployee.GetStringData: string; begin result := inherited GetStringData + ‘ ‘ + Appointment; //inherited GetStringData – вызовется метод предка, //который вернет фамилию + номер паспорта + табельный номер end;

function TTeacher.GetStringData: string; begin result := inherited GetStringData + ‘ ‘ + Chair + ‘ ‘ + Subjects; //inherited GetStringData – вызовется метод предка, // который вернет фамилию + номер паспорта + табельный номер end;

Как видно из примера, само свойство FullData не перекрывается и не переопределяется в потомках. Зато перекрывается как виртуальный его метод чтения GetStringData. Благодаря этому при чтении свойства будет вызываться метод, соответствующий реальному классу объекта, и возвращен будет соответствующий набор данных. Например:

for i := 1 to MaxPersonNum do begin OutData(Persons[i].FullData); //При чтении свойства FullData вызывается метод //GetStringData соответствующий реальному типу объекта end;