Міністерство освіти і науки України
Національний університет “Львівська політехніка”
Кафедра автоматизованих систем управління
ОБ’ЄКТНО-ОРІЄНТОВАНЕ ПРОГРАМУВАННЯ
КОНСПЕКТ ЛЕКЦІЙ
для студентів базового рівня за напрямками
“Видавничо-поліграфічна справа” (шифр – 6.09.27)
та “Комп’ютерні науки” (шифр – 6.08.04)
Львів – 2007
Об’єктно-орієнтоване програмування: Конспект лекцій для студентів базового рівня за напрямками “Видавничо-поліграфічна справа” (шифр – 6.09.27) та “Комп’ютерні науки” (шифр – 6.08.04) / Укл. Дронюк І.М., Цимбал Ю.В. – Львів: Національний університет “Львівська політехніка”, 2007. – 34 с.
Укладачі:
Дронюк Іванна Мирославівна, доц., к. ф.-м.н.,
Цимбал Юрій Вікторович, асист., к.т.н.
Відповідальний за випуск:
Шпак З.Я.
Рецензент:
Ткаченко Р.О., проф., д.т.н.
Методичні вказівки обговорено та схвалено на засіданні кафедри АСУ
Протокол № 8-06/07 від “02” березня 2007 р.
Завідувач кафедрою АСУ ____________________ Рашкевич Ю.М.
Тема 1. Історія виникнення об’єктно орієнтованого програмування (ООП).
Ключові поняття ООП.
Основні поняття
Парадигма програмування
Структурне програмування
Об’єктно-орієнтоване програмування
Об’єкт
Абстракція
Інкапсуляція
Ієрархія
Наслідування
Поліморфізм
Розвиток у галузі програмування безпосередньо залежить від задач, які пропонуються для вирішення з його допомогою. Складність задач у програмуванні неперервно зростає. Наприклад, при створенні першої версії 32-розрядної операційної системи Windows NT (1993 рік) було написано 6 млн. рядків програмного коду, а код Windows 2000 вже містить близько 29 млн. рядків. Для вирішення поставлених задач, програмісти вдосконалюють методологію програмування, тобто напрацьовують певні рекомендації, узагальнюють підходи та виробляють нові стилі програмування.
Загальноприйняті у певний період часу підходи та стилі програмування утворюють так звані парадигми програмування. Можна виділити наступну послідовність парадигм програмування:
Процедурне – Структурне – Модульне – Об’єктно-орієнтоване.
Поява нової парадигми сприяє розробці нових мов програмування та інструментальних засобів. Мови програмування Pascal та C створені за парадигмами структурного та модульного програмування. Структурні мови програмування передбачають просту схему побудови програм – як взаємодію даних та коду. Дані описують за допомогою змінних різних типів – цілочисельних, масивів, записів та ін. З даними працює код, який складається з процедур та функцій – різних для різних типів даних. При такому підході розробка великих проектів, де використовується багато різних структур даних є досить складною.
З розвитком програмування виникла ідея поєднати в межах однієї сутності дані і код, що безпосередньо опрацьовує ці дані. Така сутність отримала назву об’єкт, а відповідний підхід до створення програм називають об’єктно-орієнтованим програмуванням.
Об’єктно-орієнтоване програмування (ООП) – це парадигма програмування, яка розглядає програму як сукупність гнучко пов’язаних між собою об’єктів.
З погляду сприйняття людиною об’єктом може бути:
- відчутний і (або) видимий предмет;
- щось, що сприймається мисленням;
- щось, на що спрямовано думку або дію.
Кожен об’єкт має суттєві характеристики, які відрізняють його від усіх інших об’єктів. Сукупність таких характеристик називається абстракцією. Розрізняють абстракції стану та поведінки об’єкта.
Стан (дані об’єкта) характеризується переліком та значеннями певних ознак. Поведінка (код об’єкта) визначається набором операцій, які виконуються об’єктом, або над об’єктом.
Кожен об’єкт є екземпляром (представником) певного класу. Відповідно, клас – це певна множина об’єктів, що мають спільні характеристики стану та поведінки.
ООП – основна парадигма програмування, починаючи з 90-х рр. XX ст. Окрім розвитку методології програмування, передумовами його виникнення можна вважати:
Прогрес в області архітектури ЕОМ
Розвиток мов програмування Simula 67, Smalltalk
Розвиток теорії баз даних
Дослідження в галузі штучного інтелекту
Дослідження філософії та теорії пізнання.
Об’єктно-орієнтовані мови програмування:
- класичні: Simula 67, Smalltalk, Actor, Eiffel, Objective C;
- сучасні: C++, Object Pascal, Java, C#.
Мова програмування Object Pascal. Реалізація ООП у Object Pascal.
Мову програмування Pascal створено Ніклаусом Віртом у 1960-х роках на основі мови Algol-60.
У 1985 р. на основі версії Pascal фірми Apple був створений Оbject Pascal (розробник Ларрі Теслер). У 1989 р. фірма Borland включила об’єктно-орієнтовані можливості до системи програмування Turbo Pascal версії 5.5.
З 1994 р. Оbject Pascal використовується як основа системи програмування Delphi фірми Borland. Починаючи з версії Delphi 6 (2001 рік) Borland розширила можливості мови Object Pascal, що дозволило вважати мову Delphi новою мовою програмування. Надалі виклад матеріалу буде стосуватись як класичної мови Object Pascal так і мови Delphi.
У Оbject Pascal кожен об’єкт оголошується як змінна. Тоді клас буде типом цієї змінної. Клас містить опис змінних, що характеризують стан об’єкта (полів класу) та процедур або функцій, що описують поведінку об’єкта (методів класу).
Отже, першим слід описати клас.
type
<ім’я класу>=class
<опис даних>
<опис коду>
end;
Приклад:
type
TCircle = class // клас кіл
X, Y: Real; // поля (координати центру)
R: Real; // поле (радіус)
procedure Draw; // метод (малювання на екрані)
function Area: real; // метод (обчислення площі)
end;
Після опису структури класу можна оголосити об’єкти – екземпляри класів.
var
<ім’я об’єкта>:<ім’я класу 1>;
// …
<ім’я об’єкта>:<ім’я класу n>;
Приклад:
var aCircle1, aCircle2: TCircle; // два об'єкта-кола
Основні переваги концепції ООП:
- моделювання предметів та явищ реального світу;
- наявність типів даних, що створюються користувачем (класи);
- приховування деталей реалізації (інкапсуляція);
- можливість повторного використання коду (наслідування);
- інтерпретація викликів процедур та функцій на етапі виконання (поліморфізм).
Інкапсуляція (англ. encapsulation) – спосіб поєднати дані і код, який опрацьовує ці дані. При цьому частина даних або коду може бути захищена від зовнішнього впливу (поза об’єктом) або помилкового використання. Наприклад, можна захистити, поле R класу TCircle від надання йому від’ємних значень.
Наслідування (англ. inheritance). На основі одного класу можна створити інший, який буде використовувати (успадковувати) стан та поведінку першого. У цьому випадку перший клас називається батьківським (базовим, надкласом або предком), а другий – дочірнім (похідним, підкласом або нащадком). Окрім використання полів та методів батьківського класу, дочірній клас може їх змінювати або доповнювати, а також мати власні.
Дочірній клас оголошується наступним чином:
type
<ім’я дочірнього класу> = class(<ім’я батьківського класу>)
Наприклад:
Type
TCircle = class(TEllipse)
Поліморфізм (англ. polymorphism) – можливість позначати одним ім’ям об’єкти різних класів, які мають спільного предка. Тоді кожен об’єкт, що позначається таким поліморфним ім’ям, може по-своєму реагувати на якийсь спільний набір операцій.
Приклад. Нехай у класі TFigure (геометричні фігури) оголошено метод Draw (малювання). Змінна типу TFigure може містити різні фігури, наприклад, кола (об’єкти класу TCircle) або прямокутники (об’єкти класу TRectangle). Тоді, при виклику метода Draw у першому випадку буде намальовано коло, а у другому – прямокутник.
Тема 2. Основи мови Object Pascal
Основні поняття
Елементи програми
Структура програми
Типи даних
Структуровані типи даних
Операції
Оператори
Елементи програми:
Алфавіт символів:
- латинські літери (А..Z, a..z)
- десяткові цифри (0..9)
- шістнадцяткові цифри
- спеціальні символи: + - * / . , ; : = < > ( ) [ ] { } ^ @ $ # ’ пропуск _
- комбінації символів: := <> >= <= .. (* *) // (. .)
- комбінації літер.
Програма – послідовність символів, які об’єднуються в лексеми.
Лексеми поділяються на ключові слова, ідентифікатори, константи, комбінації символів, розділові знаки.
Ідентифікатори (стандартні та користувацькі) можуть бути іменами модулів, міток, констант, типів, змінних, процедур та функцій; складаються з латинських букв, цифр, спеціального символа _ (знак підкреслення); починаються з букви або знака підкреслення (мітки можуть починатися з цифри).
Символи кирилиці можуть використовуватись у символьних константах або коментарях.
Структура програми на Object Pascal
program <ім’я програми>;
uses <список модулів>;
label <список міток>;
const <список констант>;
type <список типів>;
var <список змінних>;
<Список процедур та функцій>
begin
<Послідовність операторів, зокрема, викликів процедур та функцій>;
end.
У будь-якому місці програми може бути коментар.
Типи даних
Прості типи
Порядкові
Цілі
Таблиця 1. Цілі типи даних в Object Pascal
Тип
Розмір
(у байтах)
Знак
Діапазон
Відповідник
у Turbo Pascal 7.0
Byte
1
беззнакове
0..255
Byte
Shortint
1
знакове
-128..127
Shortint
Word
2
беззнакове
0 .. 65535
Word
SmallInt
2
знакове
-32768 .. 32767
Integer
Cardinal
4
беззнакове
0 .. 232-1
-
Integer
4
знакове
-231 .. 231-1
Longint
Int64
8
знакове
-263 .. 263-1
-
Логічні
Boolean (значення true або false)
Символьні
Char (Ansi Char) – довжиною в 1 байт (кодування ANSI)
WideChar – довжиною у 2 байти (кодування Unicode)
Переліковий
Наприклад, Seasons = (Winter, Spring, Summer, Autumn);
Діапазон
Наприклад, Days = 1..31
Дійсні
Таблиця 2. Дійсні типи даних в Object Pascal
Тип
Розмір
(у байтах)
Діапазон
Відповідник
у Turbo Pascal 7.0
Single
4
1.5x10–45 .. 3.4x1038
Single
Real
8
5.0x10–324 .. 1.7x10308
Double
Real48
6
2.9x10-39 .. 1.7x1038
Real
Comp
8
–263+1 .. 263 –1
Comp
Currency
8
( -1015.. 1015
-
Double
8
5.0x10–324 .. 1.7x10308
Double
Extended
10
3.6x10–4951 .. 1.1x104932
Extended
Структуровані типи
Масиви (array)
Множини (set of)
Файл (file of, text)
Записи (record)
Вказівники (pointer)
Рядки (string)
Процедурні типи (procedure)
Варіантні типи (variant)
Класи (class)
Інтерфейси (interface)
Змінні та константи усіх типів використовуються у виразах.
Вираз складається з сумісних за типом операндів, які об’єднані знаками операцій.
Операції
Арифметичні
+ – * / div (ділення по модулю), mod (залишок від ділення).
Логічні
not (логічне заперечення)
and (логічне множення)
or (логічне додавання)
xor (додавання за модулем 2)
shl (побітовий зсув вліво)
shr (побітовий зсув вправо)
Над рядками + (додавання)
Над множинами + (додавання) – (віднімання) * (перетин)
Відношення
= <> > < <= >= in (елемент множини)
Визначення адреси @
Над об’єктами: is, as
Пріоритет операцій (від найвищого до найнижчого)
@, not
*, /, div, mod, and, shl, shr, as
+, –, or, xor
=, <>, <, >, <=, >=, in, is
Оператори
Задають деяку алгоритмічну дію над даними.
Прості оператори (не містять інших операторів)
присвоєння
<ідентифікатор>:= <вираз>;
виклик процедури або функції
<ім’я процедури або функції> (<список параметрів>);
безумовний перехід
goto <мітка>;
Порожній оператор
;
Структуровані оператори (побудовані з інших операторів за певними правилами)
Складений оператор
begin
<інші оператори>
end;
Умовні оператори
Умовний оператор в скороченій формі
if <умова> then <оператор>,
де <умова> – вираз типу Boolean
Умовний оператор в звичайній формі
if <умова> then <оператор1>
else <оператор2>
Оператор вибору
case <ключ вибору> of
<список1>:<оператор1>
<список2>:<оператор2>
//…
<список n>:<оператор n>
else <оператор>
end;
де <ключ вибору> – змінна або вираз одного з порядкових типів,
<список1>…<список n> – значення цієї змінної.
Оператори циклу
Оператор циклу з післяумовою
repeat
<оператор1>
// …
<оператор n>
until <умова виходу з циклу>;
Оператор циклу з передумовою
while <умова виконання циклу> do
begin
<оператор1>
// …
<оператор n>
end;
Оператор циклу з лічильником
for <змінна циклу>:= <початкове значення> to
<кінцеве значення> do <оператор>; // крок зміни = 1
або
for <змінна циклу>:= <початкове значення> downto
<кінцеве значення> do <оператор>; // крок зміни = -1
Допоміжні
continue; // перехід на наступну ітерацію циклу
break; // припинення циклу і передача керування наступному оператору
exit; // вихід з процедури або функції
Структуровані типи
Масиви.
Масив містить визначену кількість елементів одного типу. Доступ до елементів реалізується через індекси.
type
<ім’я типу>= array [<тип індексу>] of <тип елемента>;
var
<ім’я змінної>: <ім’я типу>;
// або
<ім’я змінної>: array [<тип індексу>] of <стандартний тип елемента>
Приклад:
var
A1: array [1..10] of Byte;
For i:=1 to 10 do
A1[i]:= i*2;
Зауваження. Ім’я типу для масиву необхідно задавати, якщо цей масив є аргументом процедури або функції.
Множини
Множина – набір елементів одного з порядкових типів. Множини не є впорядкованими і мають змінну кількість елементів.
Приклад:
type
TDigit = set of 0..9;
TColor = set of (Red, Green, Blue);
var k: set of Char = [‘Y’,’y’,’N’,’n’];
Key: Char;
If not (Key in K) then exit;
Записи
Запис складається з елементів, які називаються полями і можуть бути різного типу.
type
<ім’я типу>= record
<ім’я поля1>: <тип поля1>;
// …
<ім’я поля n>: <тип поля n>;
end;
Для доступу до полів запису може використовуватись спеціальний оператор with
Приклад:
type
TPoint= record
X, Y : Real;
end;
var aPoint1, aPoint2: TPoint;
begin
aPoint1.X:= 10.5;
aPoint2.Y:= -7.2;
with aPoint2 do
begin
X:= 4.2;
Y:= 6.9;
end;
end.
4.Вказівники:
Вказівники містять адреси елементів інших типів.
type <ім’я типу вказівника> = ^<ім’я іншого типу>
var
<ім’я змінної>: <ім’я типу вказівника>;
// або
<ім’я змінної>: ^<ім’я іншого типу>
Приклад
type TPoint= record
X, Y: Real;
end;
var P1:^TPoint;
begin
New(P1); //виділення пам’яті
P1^.X:=5; // доступ до даних – через розадресацію вказівника P1^.Y:=3; // (^ – англ. caret)
Dispose (P1); //звільнення пам’яті
end;
Тема 3. Класи на Object Pascal
Основні поняття
Клас
Об’єкт
Екземпляр класу
Поле
Метод
Властивість
Конструктор
Деструктор
Опис класів на Object Pascal
Тип даних – статичний опис сукупності динамічних об’єктів, які об’єднані певними операціями. В ООП кожен тип базується на певному класі. Клас – це тип даних з частковою або повною реалізацією операцій.
type <ім’я класу> = class (<ім’я батьківського класу>)
private
{поля, методи}
protected
{поля, методи, властивості}
public
{поля, методи, властивості}
published
{поля, методи, властивості}
end;
Ім’я класу – будь-який ідентифікатор; як правило, починається з великої літери Т. Ім’я надкласу може не вказуватись, тоді вважається, що клас є безпосереднім нащадком класу TObject. В ObjectPascal клас TObject є базовим класом для усіх об’єктів.
В описі класу визначають поля і методи.
Поля – це певні характеристики об’єктів класу (описують як змінні).
Методи – це операції над об’єктами класу (описують як процедури та функції).
Наступні директиви керують правами доступу до елементів класу.
Private (особистий, закритий) містить поля та методи, які використовуються лише об’єктами цього класу.
Protected (захищений) містить поля та методи, доступні також для всіх нащадків цього класу.
Public (відкритий) – поля та методи, а також властивості, доступні для зовнішнього використання, у тому числі з об’єктів інших класів.
Published (для публікації) містить поля, методи, які є відкритими, а також властивості які будуть доступні під час проектування у інспекторі об’єктів Delphi (для перегляду і зміни).
Якщо директиви не вказані, то за замовчуванням вважається, що встановлено директиву Published.
Приклад класу: TPoint – точки на площині
type
TPoint = class
X, Y: Real; // координати в декартовій системі
function Ro: Real; // координати
function Fi: Real; // в полярній системі
function Distance (p: TPoint): Real;
// відстані між двома точками;
procedure Translate (dx, dy: Real);
// переміщення;
procedure Rotate (p:TPoint; f: Real);
// обертання точки на певний кут навколо іншої точки (центру обертання);
procedure Scale (k: Real);
// масштабування
end;
В цьому описі:
X, Y – поля класу;
Ro, Fi, Distance, Translate, Rotate, Scale – методи класу.
Можливим є опис, де X та Y будуть методами, а Ro та Fi – полями:
// …
Ro, Fi: Real; // координати в полярній системі
function X: Real; // координати
function Y: Real; // в декартовій системі
У Object Pascal опис класу разом з полями та прототипами методів включається у відкриту частину програмного модуля, що називається інтерфейсом (англ. interface). Реалізація методів включається до закритої частини модуля, що називається реалізацією (англ. implementation).
implementation
function TPoint.Ro: Real;
begin
Ro:=sqrt(sqr(X)+sqr(Y));
// або
// Result:=sqrt(sqr(X)+sqr(Y));
end;
function TPoint.Fi: Real; // в полярній системі
begin
Fi:= ArcTan(Y/X);
end;
function TPoint.Distance (p: TPoint): Real;
begin
Distance:=sqrt(sqr(X-p.X)+sqr(Y-p.Y));
end;
procedure TPoint.Translate (dx, dy: Real);
begin
X:=X+dx;
Y:=Y+dy;
end;
procedure TPoint.Rotate (p:TPoint; f: Real);
var x1, y1: real;
begin
x1:=(X-p.X)*cos(f)-(Y-p.Y)*sin(f)+p.X;
y1:=(X-p.X)*sin(f)+(Y-p.Y)*cos(f)+p.Y;
X:=x1; Y:=y1;
end;
procedure TPoint.Scale (k: Real);
begin
X:=k*X;
Y:=k*Y;
end;
Опис об’єктів на Object Pascal. Створення об’єктів
Об’єкт – це екземпляр певного класу. Об’єкт описується як змінна, тип змінної – це клас об’єкта.
Приклад
var P1, P2: TPoint;
Назва об’єкта є вказівником на цей об’єкт. При описі змінних-об’єктів в пам’яті виділяється місце лише під вказівник (4-байтну адресу). Початкове значення цієї адреси дорівнює nil (невизначений вказівник).
Таким чином, об’єкти в Object Pascal є динамічними змінними, тобто створюються під час роботи програми. Але, при роботі з об’єктами, на відміну від вказівників, розадресація вказівника (^), процедури New та Dispose не використовуються.
Для створення об’єктів використовують спеціальний метод – конструктор, який позначається ключовим словом constructor. Як правило, цей метод має назву Create.
Приклад:
type TPoint= class
// …
constructor Create;
// …
end;
var P1, P2: TPoint;
constructor TPoint.Create;
Begin
X:=0;
Y:=0;
End;
begin
P1:= TPoint.Create;
P2:= TPoint.Create;
//…
end;
В даній реалізації конструктор викликається без параметрів.
Можна реалізувати конструктор у вигляді:
constructor TPoint.Create (a, b: Real);
begin
X:=a;
Y:=b;
end;
Тоді викликати конструктор можна, наприклад:
P1:=TPoint.Create (1, 2);
Всередині методів можна використовувати змінну Self, яка позначає об’єкт, для якого викликається даний метод. Зокрема, для розв’язання колізій (протиріч) між назвами полів і локальних змінних та параметрів методів можна вказати цю змінну перед назвами полів, наприклад:
Self.X:= X;
Self.Y:= Y;
Якщо конструктор реалізовано, але у ньому немає присвоєння початкових значень полям об’єкта, то відбувається ініціалізація за замовчуванням. Числові змінні ініціалізуються значеннями 0, символьні – нуль-символом (#0), логічні змінні – значенням false, вказівники – значенням nil.
Таким чином, на відміну від вказівників процедура new для створення об’єктів не використовується, якщо існує конструктор. Але змінна, яка позначає об’єкт в операціях використовується подібно до вказівників.
Ініціалізація об’єктів, звертання до полів та методів. Властивості
При написанні програми доступ до полів (для читання) не відрізняється від доступу до метода без аргументів (функції) – принцип однорідного доступу. Наприклад:
X1:=P1.X; //звертання до поля;
Ro1:=P1.Ro; //звертання до метода.
Об’єкт, для якого викликається метод, називається також одержувачем цього методу (об’єкт Р1 є одержувачем методу Ro);
Якщо потрібно змінити поле об’єкта після створення, то можна використати оператор присвоєння, наприклад
P1.X:=6; // безпосередня зміна значення поля
У багатьох випадках пряма зміна поля може бути некоректною. Наприклад, для об’єктів D1, D2 класу TDate (календарні дати) з полями Year, Month, Day:
D1.Month:= 13; D2.Day:= 32; // не коректно!
Тому, як правило, значення полів класу можуть змінювати лише методи даного класу (або дочірніх класів). Для коректного доступу до полів класу звертаються через властивості (англ. property):
property <ім’я властивості>: <тип>
read <ім’я поля або метода для читання>
write <ім’я поля або метода для запису>
default <значення за замовчуванням>;
Метод для читання – це функція без параметрів, яка повертає значення з типом властивості (наприклад GetMonth, GetDay). Також цю функцію називають селектором.
Метод для запису – це процедура, яка змінює значення певного поля при певних умовах (SetMonth, SetDay). Також ця процедура називається модифікатором. Її параметром є змінна з типом поля.
Поля та методи для читання або запису оголошуються перед описом властивості, в описі лише вказуються їх назви.
Значення за замовчуванням – це певне значення властивості, яке задається при створенні об’єкта (для полів Month та Day може бути рівним 1).
Приклад:
type TDate = class
private
aDay, aMonth, aYear: integer
protected
procedure SetDay (value: integer);
// …
published
property Day: integer read aDay
write SetDay
default 1;
end;
procedure TDay.SetDay (value: integer);
begin
if value in [1..31]
aDay:= value;
end;
Властивості – це ще один спосіб керувати доступом до полів класу (поряд з директивами private, protected, public, published).
Присвоєння об’єктів
У оператора присвоєння можливі дві семантики:
семантика копіювання;
семантика вказівників.
У першому випадку (для всіх змінних, крім об’єктів та вказівників) вміст правої частини виразу копіюється у вміст лівої частини (створюється копія у пам’яті).
У другому випадку (для об’єктів та вказівників) ліва частина буде вказувати на вміст правої частини (копія у пам’яті не створюється).
Приклад:
var P1, P2: TPoint;
P1:= TPoint.Create (3, 4); //P1.X=3; P1.Y=4;
P2:= TPoint.Create (5, 7); //P2.X=5; P2.Y=7;
P1:=P2; //P1.X=5; P1.Y=7;
P2.X:=10; //P2.X=10; P1.X=10;
В результаті присвоєння об’єкт P1 буде вказувати на вміст об’єкту P2. Попередній вміст об’єкту P1 став недосяжним.
Для створення копії об’єкта, як правило, описується і реалізується спеціальний метод Assign, який робить присвоєння кожного поля окремо за новим вказівником.
P1.Assign(P2); // P1:=P2
Перевірка на рівність
При порівняні двох змінних, аналогічно до присвоєння є дві семантики: копіювання і вказівників.
Для чисел та символьних змінних умовою рівності є однакове побітове представлення в пам’яті (семантика копіювання).
Для змінних складених типів використовується рівність елементів. Можлива ситуація, коли: а = b, але b ≠ а (коли змінна b містить більше елементів, ніж змінна а).
Приклад:
а – точка у двовимірному просторі, b – точка у тривимірному просторі.
Для об’єктів рівність досягається, коли існує рівність вказівників (семантика вказівників).
If P1=P2 then
{…}
Альтернативна перевірка на рівність (для об’єктів з різними вказівниками) може бути реалізована за допомогою спеціального метода Equals.
If P1.Equals(P2) then
{…}
Знищення об’єктів
Об’єкти – динамічні змінні, які створюються та знищуються під час роботи програми (run-time), тому знищення об’єкту передбачає звільнення пам’яті. З цією метою використовують спеціальний метод – деструктор (позначається ключовим словом destructor). Як правило, цей метод має назву Destroy.
Окрім деструктора в описі класу звичайно вводиться метод Free, який перевіряє чи змінна Self має значення nil. Для непустого об’єкта викликається деструктор.
Приклад.
procedure TPoint.Free;
begin
if Self<>nil then Destroy;
end;
При цьому автоматичного встановлення значення об’єктної змінної в nil не відбувається.
Керування пам’яттю
Для звільнення пам’яті, яка виділяється під об’єкти, які більше не потрібні, можливі 3 способи:
Ручне звільнення (засобами мов програмування);
“Автоматичне прибирання сміття” (англ. аutomatic garbage collection). Під “сміттям” розуміють об’єкти, вміст яких став недосяжним, наприклад, внаслідок присвоєння. Для різних операційних систем розроблені спеціальні процедури, які відстежують ці об’єкти з певними часовими інтервалом і знищують.
Кожному об’єкту при створенні призначається власник (інший об’єкт), який керує процесом звільнення пам’яті. (Наприклад, форма є власником всіх об’єктів, що знаходяться на ній).
Типові помилки при звільненні пам’яті
Спроба використати пам’ять, яка ще не виділена;
Виділена пам’ять ніколи не звільнюється;
Пам’ять звільняюється двома незалежними процедурами;
Спроба використати вже звільнену пам’ять.
Тема 4. Способи повторного використання коду
Основні поняття
Композиція
Наслідування
Батьківський клас
Дочірній клас
Перевизначення
Абстрактний клас
Композиція
Якщо екземпляр одного класу є полем іншого класу, вважається, що між класами є певна залежність. Її можна використати у програмі як спосіб побудови нових класів. Такий спосіб повторного використання коду називається композицією. При цьому встановлюється відношення між об’єктами цих класів – “бути частиною” (англ. pаrt of).
Приклад:
type TRectangle=class
Points: array [1..4] of TPoint; // об’єкти-точки є полями // класу прямокутників
// …
end;
var R1: TRectangle;
X1: Real;
// …
X1:=R1.Points[1].X;
Наслідування
Якщо будь-який екземпляр одного класу (T1) є водночас екземпляром іншого класу (T2), але не навпаки, то вважається, що між двома класами існує залежність: другий клас називається батьківським класом, а перший – дочірнім.
Приклад
TRectangle=class(TQuadrangle);
// …
End;
У даному прикладі TQuadrangle – батьківський клас, TRectangle – дочірній (кожен прямокутник є чотирикутником).
Такий спосіб повторного використання коду називається наслідуванням (англ. inheritance). Відношення між об’єктами цих класів – “бути”(англ. is a).
Наслідування – це можливість доступу об’єктів дочірнього класу до полів та методів батьківського класу. Наприклад, якщо поле Points оголошено в класі TQuadrangle, до нього можуть також звернутись об’єкти класу TRectangle. Такі поля та методи називаються успадкованими. Також дочірній клас може мати додаткові поля та методи
Клас може успадковувати поля та методи класів віддалених від нього на декілька рівнів.
Приклад ієрархії класів:
TPolygon → TQuadrangle → TRectangle → TSquare
Клас TSquare успадковує поля та методи класів TPolygon, TQuadrangle та TRectangle.
Перевизначення методів. Абстрактні методи та класи
Розглянемо приклад. Нехай існує наступна ієрархія класів:
TFigure → TClosedFigure → TPolygon → TQuadrangle → TRectangle → TSquare
У класах описані наступні поля та методи:
TFigure: Origin, Points (поля), Draw та Rotate (методи);
TClosedFigure Perimeter (метод);
TQuadrangle Diagonal1, Diagonal2 (методи);
TRectangle Side1, Side2 (поля або методи).
Тоді:
Поля Points та Origin, методи Draw та Rotate, успадковуються усіма дочірніми класами.
Метод Perimeter є доступним для класу TClosedFigure та його дочірніх класів.
Метод Diagonal є доступним для класу TQuadrangle та його дочірніх класів.
Дочірні класи можуть змінювати поведінку, яка успадкована від батьківського класу – інакше реалізовувати або перевизначати методи. Щоб дозволити перевизначення, у батьківському класі такий метод має бути позначений ключовим словом virtual (віртуальний метод). Метод, що перевизначається, у дочірньому класі має бути позначений ключовим словом override.
Якщо реалізація методів, які належать до класів на верхніх рівнях ієрархії є достатньо складною або неможливою і методи перевизначаються в дочірніх класах, то ці методи у батьківських класах можна описати, але не реалізовувати.
Такі методи батьківського класу називаються абстрактними і позначаються ключовим словом abstract. Клас, у якому є хоча б один абстрактний метод, називається абстрактним. Якщо абстрактним є конструктор, то об’єкти цього класу не створюють.
type
TFigure=class
//…
procedure Draw; virtual; abstract;
procedure Rotate (X, Y, Angle: Real); virtual; abstract;
end;
TClosedFigure=class(TFigure)
// …
function Perimeter: Real; virtual;
// …
end;
TPolygon=class(TClosedFigure)
//…
end;
TQuadrangle=class(TPolygon)
// …
procedure Draw; override;
procedure Rotate (X, Y, Angle: Real); override;
end;
TRectangle=class(TQuadrangle)
// …
function Perimeter: Real; override;
//…
end;
Реалізація перевизначеного методу Perimeter у класі TRectangle може бути наступною:
function TRectangle.Perimeter: Real;
begin
Perimeter:=(Side1+Side2)*2;
end;
Способи перевизначення методів
Заміщення - метод дочірнього класу (ДК) повністю заміщує метод батьківського класу (БК) Приклад – обчислення площі прямокутника у порівнянні з обчисленням площи довільної замкненої фігури.
Уточнення – метод ДК містить виклик метода БК (позначається ключовим словом inherited). Приклад – метод малювання заповненого прямокутника викликає метод малювання контура з батьківського класу прямокутників і додає код для заповнення:
type
TFilledRectangle=class(TRectangle)
{…}
procedure TFilledRectangle.Draw;
begin
inherited Draw;
Fill;
end;
Тема 5. Гнучкість коду
Основні поняття
Поліморфізм
Статичний тип
Динамічний тип
Зв’язування
Перевантаження
Поліморфізм. Принцип підстановки і поліморфне присвоєння. Статичні та динамічні типи.
Наслідування надає можливість використовувати у діях над об’єктами наступне правило (принцип підстановки):
Якщо є два класи А і В, такі, що клас В є дочірнім для класу А, то ми можемо підставити об’єкт класу В замість об’єкта класу А у будь-якій ситуації без видимої різниці.
Отже змінній-об’єкту батьківського класу завжди можна надати значення іншого типу (дочірнього класу).
Така можливість змінної або аргументу функції містити під час виконання програми значення різних типів називається поліморфізмом (англ. polymorphism); змінна – поліморфною, а надання цій змінній значення іншого типу – поліморфним присвоєнням.
Згідно з принципом підстановки поліморфне присвоєння є можливим від об’єкта ДК до об’єкта БК, але не навпаки !
Приклад:
var P1, P2: TPolygon; R1, R2: TRectangle;
// …
P1:=R1; // коректно
R2:=P2; // не коректно
Після присвоєння P1:=R1, вважається , що клас TPolygon – це статичний тип змінної P1, а клас TRectangle – динамічний тип цієї змінної.
В Object Pascal статичний і динамічний типи при оголошенні змінних збігаються. При поліморфному присвоєнні відбувається зміна динамічного типу.
Пересилання повідомлень. Зв’язування повідомлення і метода.
Пересиланням повідомлень в ООП називається динамічний процес звертання до об’єкта з вимогою виконати певну дію. Дія, яка виконується у відповідь на повідомлення не є фіксованою і може відрізнятися для об’єктів різних класів. Тобто відбувається пошук метода, який буде викликатися, або зв’язування повідомлення та метода.
Приклад:
var P1: TPolygon; R1: TRectangle;
X1: Real;
// …
P1:=R1;
X1:= P1.Perimeter; // об’єкт P1 - одержувач повідомлення Perimeter
Виклик P1.Perimeter є коректним лише тоді, коли метод Perimeter оголошений у класі об’єкта P1 (TPolygon) або в одному з його батьківських класів, тобто коректність повідомлення перевіряється по статичному типу.
Реалізація методу Рerimeter береться з класу TRectangle – за динамічним типом змінної. Тоді повідомлення Рerimeter зв’язується з реалізацією з класу TRectangle, отже зв’язування є динамічним.
Приклад:
var Figures: array [1..4] of TFigure;
R: TRectangle; T: TTriangle; E: TEllipse; S: TSquare;
//…
Figures[1]:= R;
Figures[2]:= T;
Figures[3]:= E;
Figures[4]:= S;
for i:=1 to 4 do
Figures[i].Draw;
В результаті будуть по черзі викликані 4 реалізації методу Draw з різних класів (в залежності від динамічного типу), але при цьому потрібно, щоб метод Draw був оголошений (навіть абстрактним) у класі, який відповідає статичному типу.
Визначення динамічного типу об’єкта.
Щоб дізнатися динамічний тип об’єкта, використовується метод класу TObject:
function ClassName: ShortString;
або оператор is, наприклад:
var P: TPolygon; R:TRectangle;
//…
P:=R;
if P is TRectangle then (P as TRectangle).Side1:=10;
Оператор is поверне true, коли змінна P має динамічний тип TRectangle або один з його дочірніх класів.
Оператор as при цьому виконає безпечне приведення змінної p до типу TRectangle.
Перевантаження операторів
У наведених вище прикладах, методи що викликалися, записувалися однаково для об’єктів різних класів, тобто вважалися ніби однією функцією (“чистий поліморфізм”). Можливий інший варіант, коли одне ім’я мають декілька різних функцій. Цей випадок називається перевантаженням.
У програмуванні використовують перевантаження операторів, а в ООП – перевантаження методів.
Приклад перевантаження операторів є '+':
5 + 3 = 8 // для чисел
'5' + '3' = '53' // для символьних рядків
(5) + (3) = (3, 5) // для множин
Результат буде залежати від типів аргументів.
Перевантаження методів
На мові Object Pascal усі методи, які перевантажуються, позначаються ключовим словом overload. Якщо метод, що перевантажується, оголошується в дочірньому класі, то крім ключового слова overload слід вказати ключове слово reintroduce.
Приклад:
type
TNumber=class
function Add (X, Y: Integer): Integer; overload; // метод 1
Function Add (X, Y: Real): Real; overload; // метод 2
Function Add (X: Integer; Y: Real): Real; overload; // метод 3
Function Add (X: Real; Y: Integer): Real; overload; // метод 4
End;
Var N: TNumber;
A: Integer; B: Real;
A:=N.Add(1, 2); //виклик 1 методу
B:=N.Add(1.0, 2.0); //виклик 2 методу
B:=N.Add(1, 2.0); //виклик 3 методу
B:=N.Add(1.0, 2); //виклик 4 методу
Метод, що викликається, залежить від типів аргументів, а також від послідовності і кількості. При цьому назви формальних параметрів (наприклад X, Y) значення не мають.
Тема 6. Механізми контролю над складністю програми
Основні поняття
Абстрактний тип даних
Модуль
Зачеплення
Звязність
Перша мова програмування – Assembler. У Assembler з ускладненням програмного забезпечення практично неможливо пам’ятати всю інформацію для написання та відлагодження програми. Тому виникли інші мови програмування, наприклад, Fortran (працює з підпрограмами).
Сучасний підхід (Pascal, C)
Програма розбивається на процедури та функції;
В програмі виділяються абстрактні типи даних;
Програма розділяється на модулі.
Абстрактний тип даних (АТД) – це множина елементів, описана переліком властивостей цих елементів та переліком функцій, які можна застосувати до них.
АТД задається програмістом і використовується так само як і типи даних, вбудовані в систему. Прикладами АТД є комплексні числа (Re, Im), двовимірні масиви (матриці).
Модулі (англ. units) – це окремі частини програми; мають такі особливості:
можуть компілюватися окремо, але встановлювати зв’язки з іншими модулями (через директиву uses);
імена типів, змінних процедур, функцій розділяються на дві частини: відкриту (interface) – доступну ззовні модуля, і закриту (implementation) – доступну тільки всередині модуля.
В ООП до відкритої частини відносять опис того, що може робити модуль, до закритої – реалізацію завдань модуля. Цей принцип має назву маскування інформації.
Класи поєднують можливості АТД та модулів. Так як і для АТД можна створити змінну цього типу, подібно до модулів існує поділ на приховану та видиму частину.
Характеристики модулів
Зачеплення – міра взаємозв’язків між різними модулями.
Зв’язність – міра взаємозв’язків всередині модулів.
Вважається, що сума цих мір є сталою для певної задачі, тобто чим більша зв’язність, тим менше зачеплення і навпаки.
Модуль вважається якісним, якщо зв’язність висока, а зачеплення низьке.
Різновиди зачеплення
По внутрішніх даних (один з модулів змінює локальні дані іншого).
По глобальних даних (глобальні змінні є спільними).
При керуванні (порядок виконання операції в одному модулі залежить від іншого).
По параметрах (один з модулів викликає підпрограми та функції з іншого, дані передаються як параметри).
Через підкласи (в одному з модулів створюються підкласи до класів з іншого модуля).
Найприйнятніший варіант 5, далі – 4, 3, 2, 1.
Різновиди зв’язності
За суміщенням (елементи групуються без особливої причини).
Логічна (є логіка, наприклад, бібліотека математичних функцій).
Часова (елементи використовуються приблизно одночасно, наприклад, блок ініціалізації програми).
Комунікаційна (елементи модуля працюють з одними пристроями).
Послідовна (елементи модуля використовуються в певному порядку).
Функціональна (елементи модуля виконують єдину задачу).
На рівні даних (модуль реалізує АТД) – найприйнятніший варіант.
Правило Деметера
Правило визначає, що можна і що не можна використовувати в середині класів та методів з метою підвищення зв’язності і зменшення зачеплення.
Припустимо, що слід розробити клас C із методом M.
Правило Деметера для класів:
В методі M класу С повинні використовувати виключно методи:
Самого класу С;
Класів, до яких належать аргументи метода M.
Класів глобальних та локальних змінних.
Варіант 1 є прийнятнішим за варіант 2.
Правило Деметера для об’єктів:
Всередині методу можна звертатись до об’єктів, які є:
аргументами цього метода;
одержувачами метода;
глобальними змінними;
локальними змінними.
Приклад
procedure TMyClass.Draw;
begin
Form1.Canvas.LineTo (10, 100); //не зовсім коректно;
end;
procedure TMyClass.Draw (MyCanvas: TCanvas);
begin
MyCanvas.LineTo (10, 100); // коректно (варіант 1);
end;
Тема 7. Опрацювання виняткових ситуацій
Основні поняття
Виняткова ситуація
Оператор Try
Розглянемо приклад:
Program Text;
Var a, b : integer;
Begin
Readln (a, b); // можливе некоректне введення
Writeln(a div b); // можливе ділення на 0
End.
Виняткова ситуація (вийняток) – це об’єкт класу Exception або одного з його нащадків, який описує певну нестандартну ситуацію, яка виникла в програмі. Наприклад, ділення на 0, переповнення, спроба відкрити неіснуючий файл.
Мета опрацювання виняткових ситуацій – зробити програму стійкішою, внаслідок виявлення та опрацювання помилок у простий та стандартний спосіб.
Якщо програма не перехоплює виняткову ситуацію, вона опрацьовується методом HandleException класу TApplication.
Стандартна реакція – видача повідомлення (в текстове вікно), знищення об’єкту для вийняттку, завершення роботи поточного блоку програми.
Тоді є можливою ситуація – “витік ресурсів” через те, що не виконується частина програмного блоку після помилки, тому використовують програмне опрацювання виняткових ситуацій.
Клас Exception
Від класу TObject успадковані:
Конструктор;
Метод ClassName (можна визначити клас для ситуації), наприклад: EZeroDivide, EOverflow, EConvertError, EInOutError.
2 властивості класу Exception
Message – рядок з повідомленням;
HelpContext – номер розділу конкретної довідки (по F1) у файлі допомоги.
8 конструкторів
Create
Create Fmt
Create Res
Create ResFmt
Create Help
Create FmtHelp
Create ResHelp
Create ResFmtHelp
Fmt= рядок з повідомленням є форматованим;
Res= рядок зчитується з ресурсів;
Help= розділ у файлі допомоги існує.
Для генерування виняткової ситуації використовується ключове слово raise.
raise EZeroDivide.Create (‘Ділення на нуль’);
raise EMyError.CreateFmt (‘Задано %d параметрів із %d’,[№1, №2]);
Написання захищеного коду
Написання захищеного коду передбачає виявлення та опрацювання виняткових ситуацій і коректне завершення програмних блоків.
Використовуються ключові слова: try, except, finally.
Можливі два варіанти захисту коду:
try
// код, що виконується (захищена ділянка)
еxcept
// код, який виконується при виняткових ситуаціях
end;
Приклад.
{$R+}
procedure TForm1.Button1Click (Sender: TObject);
var A, B: Integer;
C: Shortint;
begin
try
A:= StrToInt (Edit1.Text);
B:= StrToInt (Edit2.Text);
C:= A div B;
except
on EConvertError do
MessageDlg (‘Ви ввели не ціле число’, mtWarning, [mbOK], 0);
on EDivByZero do
MessageDlg (‘Ви ввели нуль’, mtWarning, [mbOK], 0);
on EIntOverflow do
if (A*B)>=0 then C:=127 else C:=-128;
end;
Edit3.Text:=IntToStr(C);
End;
try
// код, який захищається
finally
// код для усіх випадків (навіть при виняткових ситуаціях)
end;
Приклад.
procedure TForm1.Button1Click (Sender: TObject);
begin
Screen.Cursor:= crHourglass; // зміна курсору мишки
j:= 0;
try
for i:=1000 downto 1 do
j:= j + j div i; // тут можливі виняткові ситуації
finally
Screen.Cursor:= crDelault; // відновлення курсору мишки
end;
end;
Тема 8. Основи об’єктно-орієнтованого проектування
Основні поняття
ОО проектування
CRC –картки
Діаграма взаємодії
Проектування – створення проектів, які можуть мати декі...