Шупрута В.В. - Delphi 2005 - Учимся программировать (1080891), страница 27
Текст из файла (страница 27)
GetDepartment:byte;procedure SetDepartment(New_Department:byte);publicconstructor Create(Name:TName;Age:TAge;Department:byte);ГЛАВА 4 Т Объектно-ориентированное программированиеproperty Department:byteread GetDepartmentwrite SetDepartment;end;Заключенное в скобки имя класса TPersonal показывает, что класс TEmp l o y e e является производным от класса T P e r s o n a l . В свою очередь классTPersonal является базовым для класса TEmployee.Класс TEmployee должен иметь свой собственный конструктор, обеспечивающий инициализацию класса-родителя и своих полей. В листинге 4.8приведен пример реализации конструктора класса TEmployee.ЛИСТИНГ 4.8 г Конструктор ДЛЯ класса TEmployee// Конструктор для класса TEmployee.constructor TEmployee.Create(Name:TName;Age:TAge;Department:byte);begininherited Create(Name,Age); // Инициализация конструктора// класса-родителя.FDepartment:=Department;end ;В приведенном примере директивой i n h e r i t e d вызывается конструкторродительского класса.
После этого присваивается значение полю классапотомка.После создания объекта производного класса в программе можноиспользовать поля и методы родительского класса. Ниже в листинге 4.9приведен текст программы, демонстрирующий эту возможность.Листинг 4.9 • Пример реализации принципа наследованияunit WinForm;interfaceusesSystem.Drawing, System.Collections, System.ComponentModel,System.Windows.Forms, System.Data;^.typeTName = string[30];TAge = byte;TWinForm = class(System.Windows.Forms.Form){$REGION 'Designer Managed Code 1 }strict privateComponents: System.ComponentModel.Container;Buttonl: System.Windows.Forms.Button;Основные принципы ООПprocedure InitializeComponent;procedure Buttonl_Click(sender: System.Object;e: System.EventArgs);{$ENDREGION}strict protectedprocedure Dispose(Disposing: Boolean); override;private{ Private Declarations )publicconstructor Create;end;// Объявление класса.TPersonal = class ,privatefname:TName; // Значение свойства Name - имя сотрудника.fage:TAge;// Значение свойства Age - возраст сотрудника.function GetName:TName;function GetAge:TAge;procedure SetAge(new_age:TAge);public// Конструктор - создание нового объекта (экземпляра класса)constructor Creat e(Name:TName;Age:TAge);procedure Showlnfo; // Это метод класса - показ информации// о сотруднике.;// Свойства объектаproperty Name:TName // Свойство только для чтения.read GetName;property Age:TAgei// Свойство для чтения и записи.read GetAgewrite SetAge;end;// Класс TEmployee создан на основе TPersonal.TEmployee = class(TPersonal)privateFDepartment:byte; // Номер отдела сотрудника.function GetDepartment:byte;procedure SetDepartment(New_Department:byte);publicconstructor Create(Name:TName;Age:TAge;Department:byte);property Department:byteread GetDepartmentwrite SetDepartment;end;6-3166ГЛАВА 4 Т Объектно-ориентированное программирование[assembly: RuntimeRequiredAttribute(TypeOf(TWinForm))]implementation{$AUTOBOX ON}{$REGION 'Windows Form Designer generated code'}procedure TWinForm.Dispose(Disposing: Boolean);beginif Disposing thenbeginif Components <> nil thenComponents.Dispose() ;end ;inherited Dispose(Disposing);end ;// Конструктор для формы.constructor TWinForm.Create;begininherited Create;IriitializeComponent;end;procedure TWinForm.Buttonl_Click(sender: System.Object;e: System.EventArgs);varworker: TPersonal;dep_worker:TEmployee;begin// Создание нового объекта TPersonal.worker:=TPersonal.Create('Шупрута Владимир',0);// Вызов метода объекта - вывод информации о сотруднике.worker.showinfо; // На экране сообщение Шупрута Владимир О'.// Изменяем свойства объекта.worker.age:=2 5;// Вызов метода объекта - вывод информации о сотруднике.worker.showinfо// На экране сообщение 'Шупрута Владимир 25'.// Создаем сотрудника - потомка TEmployee.dep_worker:=TEmployee.Create('Захаров Сергей',24,1);// Отображаем информацию - метод унаследован от TPersonal.dep_worker.showinfо; // На экране сообщение 'Захаров Сергей 24'// Отображаем информацию - поля Name, Age// и Department.messagebox.Show(dep_worker.Name +.
' '+ dep_worker.age.tostring+ ' '+ dep_worker.FDepartment.tostring);// Удаляем объекты.Основные принципы ООП/•worker.free;dep_worker.free;end ;// Конструктор для класса TPersonal.// При вызове создается новый объект с именем Name и возрастом Age.constructor TPersonal.Create(Name:TName;Age:TAge);begininherited Create;fname:=Name;•.fage:=Age;end ;// Метод получения значения свойства Name.function TPersonal.GetName;beginresult:=fname;end;// Метод получения значения свойства Age.function TPersonal.GetAge;beginresult:=fageend;// Метод записи значения свойства Age.procedure TPersonal.SetAge(new_age:TAge);begin// Пример обработки значения на запись.// Если значение, присваиваемое свойству, <20, то// свойство не изменит своего значения.if new_age<2 0then exitelse fage:=new_age;end;// Метод класса TPersonal - процедура показа данных о сотруднике.procedure TPersonal.showinfо;beginmessagebox.Show(Naire+' '+Age.Tostring) ;end;// Конструктор для класса TEmployee.constructor TEmployee.Create(Name:TName;Age:TAge;Department:byte);begininherited Create(Name,Age); // Инициализация конструктора// класса-родителя.FDepartment:=Department;тГЛАВА 4 • Объектно-ориентированное программированиеend;// Метод получения значения свойства Department.function TEmployee.GetDepartment;beginresult:=FDepartment;end ;// Метод записи значения в свойство Department.procedure TEmployee.SetDepartment(New_Department:byte);beginFDepartment:=New_Department;end;end.Несколько слов о директиве i n h e r i t e d .
Данная директива «говорит»компилятору, что нужно вызвать метод базового (родительского) класса с тем жеименем, что и метод, в котором она указана. При этом если сигнатура методовродительского класса и его потомка совпадают (напомню, что сигнатурой методаявляется его имя и набор параметров), то указывать полный формат вызова нетребуется. Например, создадим потомка класса TEmployee, который отличаетсяот своего родителя только тем, что при вызове конструктора сразу выводитсясообщение с информацией из полей объекта:TNewEmployee = class(TEmployee)publicconstructor Create(Name:TName;Age:TAge;Department:byte);end;constructor TNewEmployee.Create(Name:TName;Age:TAge;Department:byte);begininherited;Showlnfo;end;Как видите, в конструкторе просто указывается директива i n h e r i t e d , aкомпилятор сам подставит за ней вызов конструктора родительского объекта справильными параметрами.Однако при внимательном рассмотрении листинга 4.9, а точнее - описанияобъекта TPersonal и реализации его конструктора - у вас может возникнутьследующий вопрос: как же так, в описании объекта отсутствует указание класса-родителя, а в конструкторе мы обращаемся к конструктору базового класса?Дело в том, что в Delphi все классы выстроены в единую иерархию, на вершине которой стоит класс TObject.
Когда при описании класса пропускается указание его родителя, то в этом случае компилятор считает, что родителем такого класса является TObject.Основные принципы ООПЗачем н у ж н ы д и р е к т и в ы protected и privateПомимо объявления элементов класса (полей, методов, свойств) описаниекласса, как правило, содержит директивы p r o t e c t e d (защищенный) и p r i v a t e (частный), которые устанавливают степень видимости элементов класса впрограмме.Элементы класса, объявленные в секции p r o t e c t e d , доступны только впорожденных от него классах.
Область видимости элементов класса этойсекции не ограничивается модулем, в котором находится описание класса.Обычно в секцию p r o t e c t e d помещают описание методов класса.Элементы класса, объявленные в секции p r i v a t e , видимы только внутримодуля. Эти элементы не доступны за пределами модуля, даже в производныхклассах. Обычно в секцию p r i v a t e помещают описание полей класса, а методы,обеспечивающие доступ к этим полям, помещают в секцию p r o t e c t e d .В тех случаях, когда нужно полностью скрыть элементы класса, определениекласса следует поместить в отдельный модуль, а в программу, которая используетобъекты этого класса, поместить в секции u s e s ссылку на этот модуль.ПолиморфизмИз прошлого примера вы, вероятно, заметили одну важную особенность.Класс TEmployee наследует метод Showlnfo класса TPersonal, но данный метод для класса TEmployee не очень полезен, так как отображает только частьинформации - имя и возраст.
Конечно, хотелось бы, чтобы при использовании данного метода для класса TEmployee выводилась полная информация имя, возраст и отдел. Такая замена методов при отсутствии внешних различий в вызовах может быть реализована с помощью третьей концепции ООП полиморфизма.Полиморфизм (Polymorphism) - это возможность использовать одинаковыеимена для методов, входящих в различные классы. Концепция полиморфизмаобеспечивает в случае применения метода к объекту использование именнотого метода, который соответствует классу объекта.Из прошлого примера у нас определены два класса: TPersonal и TEmployee, причем первый является базовым для второго.В базовом классе определен метод Showlnfo, обеспечивающий выводинформации (имени и возраста) о сотруднике на экран.
Чтобы дочерний класс(потомок) мог использовать метод с таким же именем, суть которогосоставляли бы несколько другие действия, данный метод в базовом классестоит объявить с директивой v i r t u a l . Объявление метода виртуальным даетвозможность дочернему классу произвести замену виртуального метода своим\№fГЛАВА 4 • Объект но-сриентированное программированиесобственным. Н и ж е в листинге 4.10 приведен пример описания базовогокласса с использованием директивы virtual.Листинг 4.10 т Метод showinf о объявлен в базовом классе как виртуальный// Объявление'класса.TPersonal = classprivatefname:TName;// Значение свойства Name - имя сотрудника.fage:TAge;// Значение свойства Age - возраст сотрудника.function GetName:TName;function GetAge:TAge;procedure SetAge(new_age:TAge);public// Конструктор - создание нового (экземпляра класса).constructor Create(Name:TName;Age:TAge);// Это метод класса - показ информации о сотруднике - виртуальный.procedure Showinfо; virtual;// Свойства объектаproperty Name:TName // Свойство только для чтения.read GetName;property Age:TAge// Свойство для чтения и записи.read GetAgewrite SetAge;end;В дочернем классе TEmployee также определен свой метод Showlnfo(листинг 4.11), который замещает соответствующий метод родительскогокласса.