Терміни “об’єкт” і “клас” знайомі кожній людині. Однак для комп’ютерників вони мають свій підтекст. Це основні поняття в об’єктно-орієнтованому програмуванні. Класи – визначається розробником тип даних, який характеризується способом їх передачі та зберігання, профілем використання і набором дій, які можуть ними виконуватись. Вони відрізняються тим, що можуть реалізовуватися в якості інтерфейсу.
Що таке ООП (об’єктно-орієнтоване програмування)
Досвідчені розробники добре знають мови COBOL і C. Написані на них програми представляли собою послідовність покрокових інструкцій. Вони використовували процедури і функції для того, щоб зробити програму модульної. Ця парадигма була зосереджена на логіці, а не на даних, та на методах їх об’єднання.
Сучасні мови програмування Delphi, Java, C# та інші слідують об’єктно-орієнтованого підходу. При цьому важливість віддається даними, а не просто написання інструкцій для виконання завдання. Об’єкт – це річ або ідея, яку ви хочете змоделювати. Їм може бути що завгодно, наприклад, співробітник, банківський рахунок, автомобіль, різні предмети обстановки і так далі.
Поняття об’єктно-орієнтованого програмування (ООП) невід’ємно пов’язано з наступними критеріями:
- Абстракція.
- Інкапсуляція.
- Спадкування.
- Поліморфізм.
Розглянемо кожен з них більш докладно.
Абстракція
Цей критерій дозволяє зосередитися на тому, що робить сам об’єкт, але не на те, якими способами ці дії реалізуються при програмуванні. ООП передбачає, що абстракція – це знання про об’єкт максимальної кількості даних. Вона допомагає у створенні незалежних модулів, які можуть взаємодіяти один з одним деякими способами.
Ми намагаємося вибірково зосередитися тільки на тих речах, які важливі для нас (у житті) або для нашого модуля (в програмуванні). Зміна одного незалежного модуля не впливає на інші. Єдине, що потрібно знати, – це те, що він нам дає. Людина, яка використовує цей модуль не повинен турбуватися про те, як завдання вирішується, що саме відбувається у фоновому режимі.
Повсякденні об’єкти, які ми використовуємо, мають абстракції, що застосовуються на різних рівнях. Одним з прикладів об’єктно-орієнтованого програмування є застосування гальмування в автомобілі. Ця система абстрактна: автолюбителю досить натиснути на педаль, щоб транспортний засіб уповільнило швидкість і зупинилося. Внесення змін у систему прискорення не впливає на гальмівну систему, так як вони незалежні. Водієві не потрібно розбиратися у внутрішній роботі гальм. Від нього вимагається лише вчасно натиснути на педаль. При цьому гальма (і дисковий, і барабанний) спрацює, а машина сповільнить швидкість.
Інкапсуляція
Ця концепція тісно пов’язана з абстракцією. Інкапсуляція – це розкриття рішення проблеми, не вимагає від користувача повного розуміння її предметної області. Вона пов’язує дані і поведінка в єдине ціле і не дозволяє клієнтові або користувачеві модуля дізнатися про внутрішнє подання, в якому реалізовано поведінка абстракції.
Дані недоступні безпосередньо. Доступ до них здійснюється через певні функції. Приховування внутрішніх елементів об’єкта захищає його цілісність, не даючи користувачам перекладати внутрішні дані компонента в неприпустиме або несумісне стан.
Спадкування
Це механізм повторного використання коду, який може допомогти зменшити його дублювання. Дана концепція є потужною функцією об’єктно-орієнтованих мов програмування. Вона допомагає організувати класи в ієрархію, дозволяючи їм наслідувати атрибути і поведінку від компонентів, що стоять вище.
Приклад наслідування: папуга – це птах, російський рубль – це вид валюти. Проте фраза “банк – це банківський рахунок” не вірна. Ця зв’язок очевидна, коли потрібно описати якусь сутність в даній постановці задачі. За допомогою спадкування можна визначити загальну реалізацію ООП і його поведінку, а потім для спеціалізованих класів скасувати або змінити ці показники на щось більш конкретне. Спадкування не працює задом наперед. Ісходник (так званий батько) не буде мати властивостей похідного (дочірнього класу).
Важливо відзначити, що при спробі змоделювати рішення не варто додавати кілька рівнів спадкування. Потрібно спробувати визначити загальні атрибути і поведінка в об’єктах, які змодельовані. Далі на основі цього можна продовжити рефакторинг коду, що визначає відповідний батьківський клас. Загальна реалізація може бути переміщена в нього.
Поліморфізм
Ця концепція дозволяє розширювати комп’ютерні системи за рахунок створення нових спеціалізованих об’єктів. Одночасно вона дає можливість поточної версії взаємодіяти з новою, не звертаючи уваги на її конкретні властивості.
Наприклад, якщо стоїть завдання написати повідомлення на аркуші паперу, можна використовувати ручку, олівець, маркер або перо. Досить того, щоб інструмент міг вміститися в руці і мав можливість залишати слід при зіткненні з папером. Виходить, що певні дії людини роблять напис на аркуші, а який при цьому використовується інструмент, це не настільки важливо для передачі інформації.
Іншим прикладом поліморфізму в системі об’єктно-орієнтованого програмування є літаків і космічний човник, які можна назвати літаючими об’єктами. Як саме вони переміщуються в просторі? Зрозуміло, що в їх роботі є велика різниця. Тобто способи реалізації їх руху неоднакові. Однак з точки зору глядача обидва об’єкта летять.
Спадкування є одним із способів досягнення поліморфізму, коли поведінка, визначене в успадкованому класі, може бути перевизначено шляхом написання користувача реалізації методу. Це називається перевизначенні (поліморфізмом часу компіляції).
Існує ще одна форма поліморфізму, звана перевантаженням, при якій спадкування не враховується. Ім’я методу буде таким же, але аргументи в методі різні.
Особливості понять “клас” і “об’єкт”
Щоб почати працювати з об’єктно-орієнтованим програмуванням, нам потрібно розібратися, що таке клас ООП і об’єкт. Важливо розуміти різницю між ними. Клас – це план для створення об’єкта. Він визначає атрибути і поведінку. Це схоже на інженерний креслення будинку. Об’єкт є екземпляром класу. Ось така між ними різниця. У наведеному нижче прикладі показано, яким чином оголошуються клас “TForml” і “Forml” на мові програмування Delphi:
type
TForml = class(TForm)
Buttonl: TButton;
procedure ButtonlClick(Sender: TObject);
end;
var
Forml: TForml;
Якщо ми хочемо змоделювати в нашій програмі, наприклад, автомобіль, то повинні визначити його атрибути: модель, паливо, марку, колір, його поведінку, а також так звані методи: запуск двигуна, гальмування, прискорення і так далі. Добре видно, що зазначені показники характерні не тільки для однієї марки або моделі транспортного засобу.
При об’єктно-орієнтованому підході ми намагаємося узагальнити наш об’єкт (машину), стверджуючи, що той, який ми збираємося змоделювати в нашій програмі, буде мати деяку кількість атрибутів і методів. Можуть бути й інші показники та характеристики транспортного засобу, але нам досить перерахованих, щоб зрозуміти, як працює клас в ООП.
Коли ми використовуємо ці дані, ми створюємо автомобіль з конкретними параметрами. Програмуючи один і той же об’єкт (машину), ми можемо взяти різні характеристики, як показано в таблиці нижче:
Об’єкт 1 | Об’єкт 2 |
модель: Ваз 2107 | модель: Ваз 2109 |
пальне: Бензин | пальне: Дизель |
колір: Червоний | колір: Зелений |
метод запуску двигуна: Start () | метод запуску двигуна: Start () |
метод гальмування: Break () | метод гальмування: Break () |
метод прискорення: Acceleration () | метод прискорення: Acceleration () |
Таким чином об’єктно-орієнтоване програмування дозволяє легко моделювати поведінку складної системи реального світу. З ООП дані і функції (атрибути та методи) об’єднуються в об’єкті. Це запобігає необхідності в будь-яких загальних або глобальних даних з ООП. Такий підхід є основною відмінністю об’єктно-орієнтованого та процедурного підходів.
Класи ООП складаються з елементів різних типів:
Методи
Поведінка класу або його примірників визначається за допомогою методів. Це підпрограми з можливістю оперувати об’єктами. Дані операції можуть змінити стан об’єкта або просто надати способи доступу до нього.
Існує безліч методів. Їх підтримка залежить від мови. Одні створюються і викликаються кодом програміста, інші (спеціальні, такі як конструктори, деструктори і оператори перетворення) створюються і викликаються згенерованим компілятором кодом. Мова може дозволити програмісту визначати ці спеціальні методи.
Інтерфейс
Це визначення групи абстрактних дій. Він з’ясовує, яке поведінка повинен демонструвати певний об’єкт без вказівки того, як воно має бути реалізовано.
Об’єкт може мати кілька ролей, а користувачі мають змогу використовувати його з різних точок зору. Наприклад, об’єкт типу “людина” може мати ролі:
- Солдата (з поведінкою “стріляти в супротивника”).
- Чоловіка (з поведінкою “люби свою дружину”).
- Платника податку (з поведінкою “плати податки”) і так далі.
Однак кожен об’єкт реалізує свою поведінку по-своєму: Міша платить податки вчасно, Андрій з простроченням, а Петро взагалі цього не робить. Те ж можна сказати про кожен об’єкт та інших ролях.
Постає питання, чому базовий клас всіх об’єктів не є інтерфейсом. Причина в тому, що в такому випадку кожен клас повинен буде реалізовувати невелику, але дуже важливу групу методів, що займе непотрібне кількість часу. Виявляється, що не всім класам потрібна конкретна реалізація – загальній за замовчуванням у більшості випадків достатньо. Немає необхідності ігнорувати будь-які методи, але якщо ситуація вимагає цього, то можливо реалізувати їх заміщення.
Хорошим прикладом є кнопки на передній панелі телевізора. Можна сказати, що вони – це інтерфейс між користувачем і електропроводкою на іншій стороні корпусу приладу. Людина натискає кнопку живлення для включення і виключення електроприладу. У цьому прикладі конкретний телевізор є екземпляром, кожен метод представлений кнопкою, а всі разом вони складають інтерфейс. У своїй найбільш поширеною формою він являє собою специфікацію групи пов’язаних методів без їх реалізації.
Конструктор
Цей критерій відповідає за підготовку об’єкта до дії, наприклад, за встановлення початкових значень для всіх його даних та їх елементів. Хоча він відіграє особливу роль, конструктор – це просто ще одна функція, з допомогою якої можна передавати інформацію через список аргументів. Їх можна використовувати для його ініціалізації. Ім’я функції конструктора класу однакові.
Наступний приклад пояснює концепцію конструктора класу на C++ (поширеній мові програмування):
#include <iostream>
using namespace std;
class Line {
public:
void setLength( double len );
double getLength( void );
Line(); // Оголошення конструктора
private: double length;
};
// Визначення функцій, включаючи конструктор
Line::Line(void) {
cout << “Об’єкт створений” << endl;
}
void Line::setLength( double len ) {
length = len;
}
double Line::getLength( void ) {
return length;
}
// Тіло програми
int main() {
Line line;
// Довжина рядка
line.setLength(6.0);
cout << “Length of line :” << line.getLength() <<endl;
return 0;
}
Коли приведений вище код компілюється і виконується, він дає наступний результат:
Об’єкт створений
Length of line: 6
Деструктор
Це спеціальна функція класу, яка знищує об’єкт, як тільки закінчується область його дії. Деструктор компілятором автоматично викликається, коли об’єкт виходить з області видимості.
Синтаксис для деструктора такий же, як і для конструктора, однак ім’я класу використовується в даному випадку для нього зі знаком тільди “~” в якості префікса.
Наступний приклад на мові C++ пояснює поняття деструктора:
#include <iostream>
using namespace std;
class Line {
public: void setLength( double len );
double getLength( void );
Line(); // Оголошення конструктора
~Line(); // Оголошення деструктора
private: double length;
}
// Визначення функцій, включаючи конструктор
Line::Line(void) {
cout << “Об’єкт створений” << endl;
}
Line::~Line(void) {
cout << “Об’єкт віддалений” << endl;
}
void Line::setLength( double len ) {
length = len;
}
double Line::getLength( void ) {
return length;
}
// Тіло програми
int main() {
Line line;
// Довжина рядка
line.setLength(6.0);
cout << “Length of line :” << line.getLength() <<endl;
return 0;
}
Коли приведений вище код скомпільовано і виконаний, він дасть наступний результат:
Об’єкт створений
Length of line: 6
Об’єкт віддалений
У чому полягають переваги класів
Переваги організації програмного забезпечення в класи об’єктів діляться на три категорії:
- Швидкий розвиток.
- Простота обслуговування.
- Повторне використання коду та дизайну.
Класи і ООП в цілому сприяють швидкій розробці, оскільки вони зменшують смисловий розрив між кодом і користувачами. Це гідно оцінили багато програмісти. Завдяки цій системі аналітики можуть спілкуватися як з розробниками, так і з користувачами, використовуючи один і той же словник, говорячи про облікові записи, клієнтів, рахунки і так далі.
Класи об’єктів часто сприяють швидкій розробці, оскільки більшість об’єктно-орієнтованих середовищ мають потужні засоби налагодження і тестування. Екземпляри класів можуть бути перевірені під час виконання, щоб переконатися, що система працює належним чином. Крім того, замість отримання дамп ядра більшість об’єктно-орієнтованих середовищ інтерпретують можливості налагодження. В результаті розробники можуть точно проаналізувати, де в програмі сталася помилка, і побачити, які методи, аргументи і значення були використані.