Об'єктно-орієнтований
підхід до програмування h2>
Об'єкт можна
порівнювати з чорним ящиком. Фокусник кладе в нього хусточку, говорить заповітне
заклинання, і витягує кролика. Так само і ми. Ми можемо ініціалізувати
об'єкт, або він сам ініціалізується значеннями за замовчуванням, викликати потрібний
метод об'єкта, і отримати результат. Нас мало цікавить те, що в ньому
конкретно відбувається, якщо об'єкт вже досить добре налагоджений. Основна ідея
об'єктно-орієнтованого підходу полягає в наявності інтерфейсу, що
служить для поліморфного поводження з об'єктом і його нащадками. За рахунок наявності
інтерфейсу легко досягається повторне використання коду. Багато програмістів,
перехідні від процедурного програмування до об'єктно-орієнтованого
програмування справедливо зауважують, що вони можуть зробити все те ж саме і
без використання об'єктів. Об'єктно-орієнтоване програмування - це
всього лише угода про правила побудови програм. Вся міць об'єктної
орієнтації розкривається у великих проектах, або при написанні великої
кількості однотипних програм, наприклад програм, що працюють з базами даних.
За рахунок повторного використання коду досягається простота в роботі
програміста (накопичення досвіду), скорочується розмір програми (методи об'єктів
одного типу або методи, успадковані від предків нащадками існують в
єдиному екземплярі), самодокументіруемость, а значить і більше простоти при
налагодження (об'єкти описуються в певному місці програми окремо від
реалізації), простота супроводу програми (не змінюючи інтерфейс об'єкта, Ви
можете змінити реалізацію методів) і т.д. Але, це тільки в ідеалі. На самом
справі достатньо просто перекрутити постулати об'єктної орієнтованості. Всі
залежить від правильності і лаконічності дерева успадкування Вашої бібліотеки
об'єктів. Нам пощастило, Ми можемо використовувати у своїй роботі останнє
досягнення в області об'єктно-орієнтованого програмування - продукт
компанії Borland-Inprise Delphi.Прі подальшому читанні тексту, якщо Вам буде
відразу щось незрозуміло, то продовжуйте читати далі. У такій складній темі
важко послідовно викласти все по порядку, тому що багато питань
переплітаються з більш складними і навпаки. По ходу читання тексту, Ви складете
повне уявлення про тему. p>
Об'єктно-орієнтоване
програмування. h2>
Об'єкт в Delphi
представляє із себе спеціальну структуру, яка описує поля, властивості та
методи об'єкта - class. Предком для всіх об'єктів служить class Tobject. Давайте
розглянемо простий об'єкт. p>
Type p>
TmyObject = class (TObject) p>
Private// закрита частина p>
AmyField: Integer;// Властивість p>
Protected// Захищена частина p>
Procedure SetMyField (Val: Integer);// Процедура запису властивості класу p>
Public// Відкрита частина p>
Constructor Create;// Конструктор p>
Destructor Destroy; override;// Деструктор p>
Property MyField: Integer read AmyField write
SetMyField;// Властивість класу p>
End; p>
Імена класів
прийнято починати з букви T, але це просто угода, а не правило. Ви можете
назвати Ваш об'єкт як хочете. Однак, буква Т на початку імені класу - це
правило хорошого тону. Далі, вказується, що цей клас є нащадком від
Tobject. Якщо Ви запишіть TmyClass = class, то все одно ваш клас буде
нащадком від Tobject. Далі, йде закрита частина інтерфейсу класу. Тут
оголошуються властивості та методи класу, які будуть доступними тільки з
методів цього ж класу, і будуть недоступними для інших класових методів і з
інших модулів програми. При спадкуванні класу, нащадок теж не буде мати
доступу до закритої частини інтерфейсу. Іноді, така поведінка класу незручно.
Наприклад, при великій кількості звернень до списку даних одного класу з
іншого через відкриту частину інтерфейсу, при кожному зверненні, можливо, будуть
перевірятися допустимі межі індексу списку. Це правильно, але може
значно сповільнити роботу програми, тому було б непогано мати
можливість для обмеженого числа класів або функцій дозволити доступ до
закритої частини, щоб вони могли звертатися до властивостей класу, оголошеним
закритій частині. Можливо, Ви писали на С + + і знаєте, що там такі класи і
функції називаються друзями. У Delphi ця можливість реалізується через
оголошення дружніх класів і функцій в одному модулі програми, тобто всі
друзі повинні бути оголошені в одному модулі. Далі, йде захищена частина. Вона
відрізняється від закритої тим, що з нащадка класу, Ви можете мати доступ до
цій частині. Далі, йде відкрита частина інтерфейсу. Тут Ви можете оголосити
властивості та методи класу, які будуть доступні для інших класів, процедур і
функцій. Є ще одна частина інтерфейсу - published (опублікована). Ця частина
має місце у нащадків від Tcomponent. Delphi використовує цю частину інтерфейсу в
інспектора об'єктів. При доступі до класу під час виконання програми, ця
частина нічим не відрізняється від public. Тут має сенс оголошувати властивості та
події класу. Всі властивості та події будуть доступні з інспектора об'єктів, і
Ви зможете редагувати їх під час розробки. Щоб працювати з класом, Ви
повинні оголосити змінну об'єктного типу цього класу, потім ініціалізувати
її викликом конструктора. p>
Type p>
TmyClass = class (TObject)// Оголошення класу p>
... p>
end; p>
... p>
Var p>
AmyClass:
TmyClass;// Оголошення змінної класу p>
begin p>
AmyClass: = TmyClass.Create;
//Виклик конструктора, зверніть увагу на те, що викликається конструктор// TmyClass.Create, а не AmyClass.Create p>
Try p>
... p>
finally p>
AmyClass.Free;// Знищення класу p>
End; p>
end; p>
Класи в Delphi
можуть створюватися тільки у динамічній пам'яті, тому всі змінні
об'єктного типу - це покажчики на екземпляр класу в динамічній пам'яті.
Типове ім'я конструктора - Create, типове ім'я деструктора - Destroy.
Дотримуйтеся цього правила, якщо немає особливої необхідності у зворотному. p>
Ініціалізація
і руйнування об'єктів h2>
Для оголошення
конструктора використовується зарезервоване слово constructor, після якого
йде ім'я конструктора і параметри, якщо необхідно. Конструктор повертає
вказівник на екземпляр класу. У конструктора Tobject ім'я Create, тому у
всіх нащадків цього класу є конструктор Create, хоча, у деяких класів
є й інші конструктори з іншими іменами, наприклад у обробників
винятків. У тілі конструктора Ви можете викликати конструктор предка для
ініціалізації закритій частині предка значеннями за замовчуванням, наприклад: p>
unit MyUnit; p>
interface p>
Type p>
TmyClass = class (TComponent) p>
... p>
public p>
constructor Create (AOwner: TComponent); override;// перевантажує конструктор предка p>
... p>
end; p>
implementation p>
Constructor TmyClass.Create (AOwner: TComponent); p>
Begin p>
Inherited
Create (Aowner);// Виклик конструктора предка p>
...//
Подальша ініціалізація об'єкта p>
End; p>
Якщо ім'я
конструктора предка збігається з ім'ям нащадка, то можна скоротити запис при
виклику конструктора предка в конструкторі нащадка: p>
Constructor TmyClass.Create (AOwner: TComponent); p>
Begin p>
Inherited
(Aowner);// Виклик конструктора предка p>
...//
Подальша ініціалізація об'єкта p>
End; p>
Для знищення
об'єкта служить деструктор. Деструктор оголошується за допомогою закритого
слова destructor, після якого йде ім'я деструктора. Деструктор нічого не
повертає і не має параметрів. Я раджу Вам замість прямого виклику
деструктора використовувати метод Free. Цей метод є у всіх класів в Delphi,
тому що успадковується від Tobject. Цей метод спочатку перевіряє нерівність покажчика
на клас nil, а тільки потім викликає Destroy. Це більш безпечний спосіб
знищити об'ект.unit
MyUnit; p>
interface p>
Type p>
TmyClass = class (TComponent) p>
... p>
public p>
constructor Create (AOwner: TComponent); override;// перевантажує конструктор предка p>
destructor Destroy; override// перевантажує деструктор предка p>
... p>
end; p>
implementation p>
... p>
destructor TmyClass.Destroy; p>
Begin p>
...//
Знищення об'єкта p>
Inherited
Destroy;// Виклик деструктора предка, для знищення закритих полів предка p>
End; p>
Інкапсуляція
h2>
Суть
об'єктно-орієнтованого підходу до програмування полягає в трьох
принципах: інкапсуляції, успадкування і поліморфізм. Для більш надійної роботи
програми, Ви не повинні безпосередньо звертатися до полів об'єкта. Замість цього, Ви
повинні звертатися через спеціальні методи, які можуть перевірити, наприклад,
на допустимість значення в заданому інтервалі або одночасно виконати
додаткову роботу по зміні стану об'єкта, в залежності від
зміни значення поля - це і є інкапсуляція. Така можливість
реалізується в класах через оголошення властивостей об'єкта. Властивість дозволяє
обмежити права на зміну значення поля і організовує доступ до поля через
методи. Ось приклад опису класу з властивістю: p>
Type p>
TmyClass = class (TObject) p>
Private p>
AmyField:
Integer;// Оголошення поля цілого типу p>
Protected p>
Procedure
SetMyField (Val: Integer); virtual;// Оголошення процедури для запису значення
властивості p>
Public p>
Property MyField: Integer read AmyField write
SetMyField;// оголошення властивості p>
End; p>
Тут ми бачимо,
що властивість MyField є цілим типом. Воно доступне для читання і запису,
тому що оголошені методи read і write. Процедура, що організує запис значення
оголошена в секції protected для того, щоб у разі необхідності, Ви могли
перевантажити її в нащадку. Насправді значення властивості зберігаються в полі
AmyField класу. Якщо Ви не оголосіть методу write, то властивість стане доступним
тільки з читання, аналогічно і з методом read. До речі кажучи, поля для
зберігання значення властивості може і не бути, головне, щоб були оголошені
методи доступу до нього. Для прикладу, клас, який реалізує інтерфейс доступу до
властивостям дисплея, міг би мати властивості ширина і висота в пікселях. Вам не
обов'язково зберігати ці значення, тому що можна викликати стандартну функцію
Windows і дізнатися ширину і висоту екрану, тим більше, що в процесі роботи ці
значення можуть мінятися при перемиканні в інший режим. Методи запису та читання
властивості підкоряються жорсткими правилами. Так для читання властивості, Вам необхідно
оголосити функцію без формальних параметрів, що повертає значення того ж типу,
що і властивість. Для запису значення, Вам необхідно оголосити процедуру з одним
параметром того ж типу, що і властивість. Якщо в якості методу для запису або
читання властивості Ви вказуєте ім'я поля для зберігання значення властивості, то це
аналогічно прямого доступу до цього поля. При компіляції такого способу
звернення до властивості, код буде оптимізований, тому це не спричинить ніяких
додаткових витрат ресурсів комп'ютера. Звичайно, такий метод звернення до
полю застосовують для читання. Методи доступу до полів класу можуть виконувати
додаткову роботу за перевстановлення значення поля класу. Так, при
установці властивості Ttable.Active в true проводиться читання даних з таблиці
бази даних на жорсткому диску, кілька разів змінюється стан об'єкта,
надсилаються події пов'язаних об'єктів, викликаються обробники делегованих
подій, які пише програміст, і лише потім встановлювати заново значення
властивості в true. Для присвоєння властивості значення за замовчуванням, використовується
ключове слово default, наприклад: p>
property
Visible: boolean read Avisible write SetVisible default true; Це означає, що при запуску програми,
компілятор встановити цю властивість в true. Однак я наполегливо раджу Вам
встановлювати значення властивостей за умовчанням в конструкторі класса.Ви можете
мати індексовані властивості. Ось приклад реалізації такого класу. P>
Type p>
TmyClass = class (TObject); p>
private p>
AmyList: Tlist;// Контейнер покажчиків p>
Protected p>
Function GetMyList (Index: Integer): Pointer;// Функція доступу з читання p>
Procedure SetMyList (Index: Integer; Val: Pointer);// Процедура доступу по запису p>
Public p>
... p>
Property MyList [Index: Integer]: Pointer read
GetMyList write SetMyList;// Оголошення індексованого властивості p>
End; p>
Тут ми бачимо,
що властивість MyList
індексованої - це елемент списку покажчиків. У квадратних дужках Вам потрібно
вказати список індексів і їх типів. У загальному випадку, індексом може бути навіть
рядок символів. Далі йде тип властивості і методи запису та читання. Функція
читання повинна мати список формальних параметрів з усіма індексами властивості та
повертати значення того ж типу, що і властивість. Процедура запису повинна мати
список формальних параметрів з усіма індексами властивості і параметр для
передачі встановленого значення того ж типу що і властивість. Велике
значення має послідовність зазначення індексів і обов'язковість передачі
значення властивості у процедурі запису останнім в списку формальних параметрів.
Якщо індексовані властивість є основним і звернення саме до нього
проводиться частіше за інші, то можна оголосити його як default, тоді не потрібно
вказувати ім'я властивість для доступу до нього, наприклад: p>
Type p>
TmyClass = class (TObject); p>
private p>
AmyList: Tlist;// Контейнер покажчиків p>
Protected p>
Function GetMyList (Index: Integer): Pointer;// Функція доступу з читання p>
Procedure SetMyList (Index: Integer; Val: Pointer);// Процедура доступу по запису p>
Public p>
... p>
Property MyList [Index: Integer]: Pointer read
GetMyList write SetMyList; default;// Оголошення індексованого властивості за замовчуванням p>
End; p>
... p>
Var p>
MyClass: TmyClass; p>
Begin p>
MyClass: = TmyClass.Create; p>
... p>
MyClass [3]: = AnyObject;// Аналогічно MyClass.MyList [3]]: = AnyObject; p>
... p>
End; p>
Значення
інкапсуляції в об'єктно-орієнтованому програмуванні важко переоцінити.
Чого вартий хоча б те, що в Delphi до 100% полів класів доступ організований
через властивості. p>
Спадкування h2>
Якщо Ви хочете
змінити або доповнити поведінка вже існуючого класу, то немає ніякої
необхідності переписувати клас заново. Вам варто скористатися
успадкуванням. Ви повинні оголосити, що новий клас є нащадком вже
існуючого і додати новий клас властивості і методи, які Вам необхідні
або перекрити існуючі методи і властивості: p>
Type p>
TmyFirstClass = class (TObject) p>
Private p>
Protected p>
Public p>
Constructor Create (Val: Integer); virtual; p>
end; p>
TmySecondClass = class (TMyFirstClass) p>
Private p>
AmyField: string;// Додали нове поле p>
Protected p>
Procedure SetMyField (Val: string);// Додали процедуру p>
Public p>
Constructor Create (Val: Integer); override;// перевантажили конструктор p>
Property MyField: string read AmyField write
SetMyField;// Додали властивість p>
End; p>
Є декілька
правил області видимості об'єкта, які допоможуть Вам розібратися зі способами
доступу до об'єктів та спадкування об'єктів: p>
Поля, властивості
і методи секції public не мають обмежень на видимість. p>
Поля, властивості
і методи секції private, доступні тільки в методах класу і у функціях,
оголошених в тому ж модулі, де й клас. p>
Поля, властивості
і методи секції protected теж доступні тільки з методів класу і функцій,
оголошених в модулі, але вони доступні в класах, які є нащадками, у тому
числі й оголошених в інших модулях. p>
При описі
нащадків, Ви можете змінювати область видимості методів і властивостей. Можна
розширювати область видимості, але не звужувати. Тобто якщо є властивість у секції
private, ви можете зробити його public, але не навпаки. Ось приклад розширення
області видимості: p>
Type p>
TmyClass = class (TObject) p>
Private p>
AmyField: Integer; p>
protected p>
property MyField: Integer read AmyField; p>
... p>
End; p>
TmySunClass = class (TMyClass) p>
... p>
Public p>
Property
MyField;// Тільки згадали його в іншій секції і він поміняв область
видимості. p>
End; p>
Успадковані
поля і методи нащадка є і в пращура. При збігу імен предка і нащадка
говорять про перекриття нащадком полів або методів предка. За способом виклику
методи класу можна розділити на статичні, віртуальні, динамічні,
перевантажуються й абстрактні. Абстрактні методи не мають реалізації і повинні
бути обов'язково перекриті в нащадках. Абстрактними можуть бути тільки
віртуальні та динамічні методи, наприклад: p>
Type p>
TmyClass = class (TObject) p>
... p>
protected p>
procedure MyMethod (Val: Integer); virtual; abstract; p>
... p>
End; p>
Статичні
методи та поля в об'єктах-нащадках перекриваються однаково: Ви можете без
обмежень перекривати старі імена і при цьому змінювати тип методів, тому що при
перекритті буде створено нове поле або метод з тим же ім'ям. Перекритий метод
предка доступний у нащадку через зарезервоване слово inherited.Type p>
TmyClass = class (TObject) p>
... p>
protected p>
procedure MyMethod; p>
... p>
End; p>
TmySunClass = class (TmyClass) p>
... p>
protected p>
procedure MyMethod (Val: Integer); p>
... p>
End; p>
... procedure TmySunClass.MyMethod (Val: Integer); begin p>
inherited
MyMethod;// Метод предка без параметрів, а метод нащадка вже з параметром,
тобто ми поміняли тип процедури. p>
... p>
end; p>
За замовчуванням
всі методи - статичні, тому їх адреси відомі вже на стадії компіляції,
вони будуть викликатися при виконанні програми найшвидшим способом.
Віртуальні і динамічні методи описуються за допомогою спеціальних директив
virtual або dynamic. Ці методи можуть бути перекриті в нащадку однойменними
методами, які мають той же тип. p>
І нарешті, хочу
сказати, що Delphi не підтримує множинного спадкоємства. Для досягнення
того ж самого, Вам прийде?? я використовувати просте спадкування від одного з
потрібних класів, а другий клас додати як поле, і організувати доступ до властивостей
другого класу через властивості спадкоємця. Так можна з'єднати декілька
класів. Інший спосіб - це створити клас-контейнер, в якому як
полів будуть потрібні включені класи, а інтерфейс доступу до них організувати
через властивості контейнера. p>
Поліморфізм h2>
Покажчик на
примірник об'єктного типу може бути присвоєно адресу будь-якого примірника будь-якого з
дочірніх типів. При зверненні до властивостей і методів через цей покажчик буде
доступний саме екземпляр, адреса якого було визначено, а не предок. Це і є
поліморфізм. Тобто Ви можете мати доступ до нащадка через покажчик об'єктного
типу предка. Розглянемо приклад: p>
Type p>
TMyClass = class (TObject) p>
... p>
public p>
procedure GetData: string; virtual; abstract; p>
... p>
end; p>
TmySun1Class = class (TMyClass) p>
Protected p>
AmyField: string; p>
... p>
public p>
procedure GetData: string; override; p>
... p>
end; p>
TmySun2Class = class (TMyClass) p>
Protected p>
AmyField: Integer; p>
... p>
public p>
procedure GetData: string; override; p>
... p>
end; p>
... p>
implementation p>
procedure TmySun1Class.GetData: string; p>
begin p>
Result: = AmyField; p>
end; p>
procedure TmySun2Class.GetData: string; p>
begin p>
Result: = IntToStr (AmyField); p>
end; p>
... p>
Var p>
MyClass: TmyClass; p>
Class1: TmySun1Class; p>
Class2: TmySun2Class; p>
Begin p>
Class1: = TmySun1Class.Create; p>
Class2: = TmySun2Class.Create; p>
... p>
MyClass: = Class1; p>
Label1.Caption: = MyClass.GetData; p>
MyClass: = Class2; p>
Label2.Caption: = MyClass.GetData; p>
end; p>
Якщо подивитися
на цей код уважно, то можна зрозуміти, що у компілятора немає можливості
визначити метод якого саме класу потрібно викликати. Тому, для визначення
адреси методу використовуються спеціальні таблиці, де зберігаються адреси на
віртуальні та динамічні методи: VMT - таблиця віртуальних методів і DMT --
таблиця динамічних методів. Коли компілятор зустрічає вказівник на
віртуальний метод, то він шукає його адресу в VMT, де зберігаються всі адреси
віртуальних методів класу успадкованих і перекритих, тому така таблиця
займає багато пам'яті, хоча і спосіб виклику методу працює порівняно
швидко. Динамічні методи викликаються повільніше, але займають менше пам'яті,
тому що в них зберігаються адреси динамічних методів тільки даного класу і їх
індекси. При виклику динамічних методів проводиться пошук по цій таблиці, якщо
метод не знайдено, то пошук продовжується в DMT предків аж до Tobject, де
викликається стандартний обробник виклику динамічних методів. Навіщо ж нам все
це треба? При проектуванні ієрархії класів предметної області, потрібно
статичними робити методи, які не змінюють своєї поведінки в нащадках, тобто
при більш детальному розгляді явища. Динамічні і віртуальні методи
можуть змінюватися при переході від загального до конкретного. Згадайте клас Tfield,
який є спільним предком для всіх класів-полів таблиці. Нащадки цього
класу реалізують доступ до стовпчиків таблиці різних типів від цілого числа до
BLOB масиву, однак, Ви можете мати зручний доступ до цих нащадкам через
вказівник типу Tfield і працювати з ними однаково. p>
Перевантаження
методів, процедур і функцій h2>
Перевантаження
оголошується за допомогою зарезервованого слова overload. Розглянемо приклад: p>
Type p>
TmyDateClass = class (TObject) p>
private p>
Adate: TdateTime; p>
... p>
Public p>
Procedure SetDate (Val: TDateTime); overload;// Оголошуємо можливість перевантаження p>
... p>
end; p>
TmySecondDateClass = class (TmyDateClass) p>
private p>
Adate: TdateTime; p>
... p>
Public p>
Procedure SetDate (Val: string); overload;// Оголошуємо можливість перевантаження p>
... p>
End; ... p>
implementationProcedure TmyDateClass. SetDate (Val:
TDateTime); p>
Begin p>
Adate: = Val; p>
End; p>
Procedure TmySecondDateClass.SetDate (Val: string); p>
Begin p>
Adate: = StrToDate (Val); p>
End; p>
Під час роботи
програми, ви можете використовувати в другому класі обидва методи SetDate. Якщо Ви передасте як параметр
рядок, то буде викликаний метод другого класу, а якщо TdateTime, то метод
предка. Можна перевантажувати і віртуальні методи, тільки замість override потрібно
використовувати reintroduce, наприклад: p>
Type p>
TmyDateClass = class (TObject) p>
private p>
Adate: TdateTime; p>
... p>
Public p>
Procedure SetDate (Val: TDateTime); overload; virtual;
//Оголошуємо можливість перевантаження p>
... p>
end; p>
TmySecondDateClass = class (TmyDateClass) p>
private p>
Adate: TdateTime; p>
... p>
Public p>
Procedure SetDate (Val: string); reintroduce; overload;
//Оголошуємо можливість перевантаження p>
... p>
End; Ви можете використовувати перевантаження і для
процедур і функцій не обов'язково при спадкуванні, і навіть процедур і функцій не
класового типу, наприклад: p>
Function Myfunction (Val: string): string; overload; p>
Begin p>
Result: = Val + 'Ok!' p>
End; p>
Function Myfunction (Val: Extended): extended;
overload; p>
Begin p>
Result: = Val/2; p>
End; p>
Або p>
TmyDateClass = class (Tobject) p>
private p>
Adate: TdateTime; p>
... p>
Public p>
Procedure SetDate (Val: TDateTime); overload;// Оголошуємо можливість перевантаження p>
Procedure SetDate (Val: string); overload;// Оголошуємо можливість перевантаження p>
... p>
End; p>
Параметри по
замовчуванням p>
Якщо Вам потрібно
створити функцію, яка в якості параметрів майже завжди отримує одне і те
ж значення, але все-таки іноді воно може змінюватися, то Вам потрібно оголосити
параметри за умовчанням як формальних параметрів, наприклад: p>
Procedure MyProcedure (Val1: Extended; Val2: Integer =
2); p>
Begin p>
... p>
End; p>
Тоді Ви
зможете викликати її такими способами: p>
MyProcedure (42.33);// аналогічно MyProcedure (42.33, 2); p>
MyProcedure (15.6,
8); p>
Правило свідчить,
що всі параметри за замовчуванням повинні бути зосереджені в кінці списку
формальних параметрів процедури або функції. Опускати параметри за замовчуванням,
можна лише починаючи з кінця списку, тому потрібно впорядковувати параметри по
замовчуванням за ступенем важливості. p>
Делегування
h2>
Подія - це
властивості процедурного типу, призначені для створення для користувача
реакції на зовнішні впливи. Події в Delphi реалізується за рахунок створення
поля процедурного типу та оголошення відповідного властивості класу, наприклад: p>
Type p>
TmyEvent = procedure (Sender: Tcomponent); of object;
//Визначення процедурного типу p>
TmyClass = class (Tcomponent) p>
Private p>
FmyEvent: TmyEvent; p>
Protected p>
Procedure DoMyEvent; p>
published p>
property OnMyEvent: TmyEvent read FmyEvent write
FmyEvent; p>
end; p>
Припустимо, Ви
визначили функцію function MyProcedure (Sender: Tcomponent) для обробки
події за допомогою інспектора об'єктів або написали вручну і нальоту присвоїли
об'єкту: MyClass.OnMyEvent: = MyProcedure. При настанні певних умов
Ваш клас може викликати процедуру DoMyEvent, де буде викликана Ваша процедура
MyProcedure так: p>
Procedure TmyClass. DoMyEvent; p>
Begin p>
If Assigned (FmyEvent) then FmyEvent (Self); p>
End; Ми бачимо, що був перевірений вказівник на
налаштовувану процедуру обробки події, і якщо він дійсний, то
викликається користувальницька процедура - це і є делегування. Зверніть
увагу, що я розмістив властивість OnMyEvent в секції published для того, щоб
програміст міг скористатися інспектором об'єктів для написання процедури
обробки події. p>
Список
літератури h2>
Банников. Н.А.
Об'єктно-орієнтований підхід до програмування. P>
Для підготовки
даної роботи були використані матеріали з сайту www.stikriz.narod.ru
. p>