Клас (програмування)
В об'єктно-орієнтованому програмуванні, клас — це спеціальна конструкція, яка використовується для групування пов'язаних змінних та функцій. При цьому, згідно з термінологією ООП, глобальні змінні класу (члени-змінні) називаються полями даних (також властивостями або атрибутами), а члени-функції називають методами класу. Створений та ініціалізований екземпляр класу називають об'єктом класу. На основі одного класу можна створити безліч об'єктів, що відрізнятимуться один від одного своїм станом (значеннями полів).
В Unified Modeling Language класи зображуються діаграми класів.
Класи та екземпляри класів
Клас можна порівнювати з формою для випічки печива — форма одна, а печива можна випекти безліч. Печиво — це конкретні об'єкти, екземпляри класу печиво, яке може бути з різною начинкою. Поля дозволяють вмістити дані про певний реальний об'єкт, а методи здійснювати обробку цих даних. Наприклад, можна створити загальний клас Людина з полями Ім'я та Прізвище, рік народження, професія, зарплата. При створенні ж на основі класу конкретного екземпляру, дані поля заповнюються конкретними даними про певну людину. Обробкою цих даних можуть займатися відповідні методи. Наприклад, можна створити метод для обчислення віку людини, тощо.
На основі класів можна створювати підкласи, які успадковують властивості та поведінку батьківських класів. Таким чином можна створити цілу ієрархію класів. Різні мови дещо по різному реалізовують механізм успадкування. Існує множинне та одинарне успадкування. Множинне — це, коли підклас створюється на основі кількох безпосередніх батьків (як то в мові програмування C++). Одинарне успадкування — це коли клас може мати одного безпосереднього батька (мова програмування Java). Надкласи можуть мати свої надкласи, підкласи можуть також бути надкласами для певних класів.
Методи класів
Через методи реалізується поведінка об'єктів. Практично вся робота з об'єктами відбувається через методи. Вони можуть змінювати стан об'єкта або ж просто надавати доступ до даних, які були інкапсульовані в об'єкті. Існує кілька видів методів, які мають деякі відмінності в різних мовах програмування. До методів та полів даних можна надавати різні права доступу, від яких залежатиме доступ до них з різних частин програмного коду. Права доступу та вид методів задаються модифікаторами при описі методів. Метод, який проводить створення та початкову ініціалізацію екземпляра класу називають конструктором класу. Метод, який проводить знищення об'єкта, називають деструктором класу.
Інтерфейс та реалізація, спадкування реалізації
У програмуванні існує поняття програмного інтерфейсу, що означає перелік можливих обчислень, які може виконати та чи інша частина програми, включаючи опис того, які аргументи і в якому порядку потрібно передавати на вхід алгоритмам з цього переліку, а також що і в якому вигляді вони будуть повертати. Абстрактний тип даних інтерфейс придуманий для формалізованого опису такого переліку. Самі алгоритми, тобто дійсний програмний код, який буде виконувати всі ці обчислення, інтерфейсом не задається, програмується окремо та називається реалізацією інтерфейсу.
Програмні інтерфейси, а також класи, можуть розширюватися шляхом спадкування, яке є одним з важливих засобів повторного використання готового коду в ООП. Успадкований клас або інтерфейс буде містити в собі все, що зазначено для всіх його батьківських класів (в залежності від мови програмування та платформи, їх може бути від нуля до нескінченності). Наприклад, можна створити свій варіант текстового рядка шляхом успадкування класу «мій рядок тексту» від вже існуючого класу «рядок тексту», при цьому передбачається, що програмісту не доведеться заново переписувати алгоритми пошуку та інше, оскільки вони автоматично будуть успадковані від готового класу, і будь-який екземпляр класу «мій рядок тексту» може бути переданий не лише в готові методи батьківського класу «рядок тексту» для проведення потрібних обчислень, а й взагалі в будь-який алгоритм, здатний працювати з об'єктами типу «рядок тексту», оскільки екземпляри обох класів сумісні по програмним інтерфейсам.
Клас дозволяє задати не лише програмний інтерфейс до самого себе і до своїх екземплярів, але і в явному вигляді написати код, відповідальний за обчислення. Якщо при створенні свого нового типу даних успадковувати інтерфейс, то ми отримаємо можливість передавати примірник свого типу даних в будь-який алгоритм, який вміє працювати з цим інтерфейсом. Однак нам доведеться самим написати реалізацію інтерфейсу, тобто ті алгоритми, якими буде користуватися цікавий нам алгоритм для проведення обчислень з використанням нашого екземпляра. Водночас, при наслідуванні класу, автоматично успадковується готовий код під інтерфейс (це не завжди так, батьківський клас може вимагати реалізації якихось алгоритмів в дочірньому класі в обов'язковому порядку). В такій можливості успадковувати готовий код і проявляється те, що в об'єктно-орієнтованої програми тип даних клас визначає одночасно як інтерфейс, так і реалізацію для всіх своїх екземплярів.
Стан об'єкта, поняття областей доступу, конструктори
Однією з проблем структурного програмування, з якою бореться ООП, є проблема підтримки правильного значення змінних програми. Часто різні змінні програми зберігають логічно пов'язані значення, і за підтримання цієї логічної зв'язності несе відповідальність програміст, тобто автоматично зв'язність не підтримується. Прикладом може слугувати пара прапорців «звільнений» та «очікує премії за підсумками року», коли за правилами відділу кадрів людина може бути водночас не звільненою і не очікувати премію, або не звільненою та очікувати премію, або звільненою і не очікувати премію, але не може бути одночасно звільненою і очікувати премію. Тобто будь-яка частина програми, яка проставляє прапорець «звільнений», завжди повинна знімати прапорець «чекає премії за підсумками року».
Хороший спосіб вирішити цю проблему — оголосити прапорець «звільнений» недоступним до зміни для всіх ділянок програми, крім одного спеціально обумовленого. У цій спеціально обумовленій ділянці все буде написано один раз і правильно, а всі інші повинні будуть звертатися до цієї ділянки завжди, коли вони хочуть встановити або зняти прапорець «звільнений».
В об'єктно-орієнтованій програмі прапорець «звільнений» буде оголошено приватним членом деякого класу, а для його читання та зміни будуть написані відповідні публічні методи. Правила, що визначають можливість або неможливість безпосередньо змінювати будь-які змінні, називаються правилами завдання областей доступу. Слова «приватний» та «публічний» в цьому випадку є так званими «модифікаторами доступу». Вони називаються «модифікаторами» тому, що в деяких мовах вони використовуються для зміни раніше встановлених прав при спадкуванні класу. Спільно класи та модифікатори доступу задають область доступу, тобто у кожної ділянки коду, залежно від того, до якого класу вона належить, буде своя область доступу щодо тих чи інших елементів (членів) свого класу та інших класів, включаючи змінні, методи, функції, константи, тощо. Існує основне правило: ніщо в одному класі не може бачити приватних елементів іншого класу. Щодо інших, більш складних правил, у різних мовах існують інші модифікатори доступу та правила їх взаємодії з класами.
Майже кожному члену класа можна встановити модифікатор доступу (за винятком статичних конструкторів та деяких інших речей). У більшості об'єктно-орієнтованих мов програмування підтримуються такі модифікатори доступу:
- private (закритий, внутрішній член класу) — звернення до члену допускаються лише з методів того класу, у якому цей член визначений. Будь-які спадкоємці класу вже не зможуть отримати доступ до цього члену. Спадкування за типом private забороняє доступ з дочірнього класу до всіх членів батьківського класу, включаючи навіть public-члени (С++);
- protected (захищений, внутрішній член ієрархії класів) — звернення до члена допускаються з методів того класу, у якому цей член визначений, а також з будь-яких методів його класів-спадкоємців. Спадкування за типом protected робить всі public-члени батьківського класу protected-членами класу-спадкоємця (С++);
- public (відкритий член класу) — звернення до члена допускаються з будь-якого коду. Спадкування за типом public не міняє модифікаторів батьківського класу (С++);
Проблема підтримки правильного стану змінних актуальна і для вихідного моменту виставлення початкових значень. Для цього в класах передбачені спеціальні методи/функції, звані конструкторами. Жоден об'єкт (екземпляр класу) не може бути створений інакше, як шляхом виклику на виконання код конструктора, який поверне створений та правильно заповнений примірник класу. У багатьох мовах програмування тип даних «структура», як і клас, може містити змінні та методи, але екземпляри структур, залишаючись просто розміченими ділянками оперативної пам'яті, можуть створюватися в обхід конструкторам, що заборонено для примірників класів (за винятком спеціальних виняткових методів обходу всіх подібних правил ООП, передбачених в деяких мовах та платформах). У цьому виявляється відмінність класів від інших типів даних — виклик конструктора обов'язковий.
Практичний підхід
У сучасних об'єктно-орієнтованих мовах програмування (в тому числі в php, Java, C++, Oberon, Python, Ruby, Smalltalk, Object Pascal) створення класу зводиться до написання деякої структури, що містить набір полів та методів (серед останніх особливу роль грають конструктори, деструктори, фіналізатори). Практично клас може розумітися як якийсь шаблон, за яким створюються об'єкти — екземпляри цього класу. Усі примірники одного класу створені за одним шаблоном, тому мають один і той же набір полів та методів.
Приклади створення класів на різних мовах програмування
Класи в мові Object Pascal (Delphi) |
---|
На мові Delphi клас описується таким чином: TMyClass = class(TObject)
private
{Описані в цій секції елементи не доступні ззовні (за межами класу, але доступні в межах модуля).}
{Тут зазвичай знаходяться поля класу.}
protected
{Описані в цій секції елементи доступні лише класу і всім його нащадкам.}
public
{Описані в цій секції елементи доступні всім.}
published
{Описані в цій секції елементи доступні всім і відображаються в Object Inspector'e.}
end;
Створюється екземпляр (об'єкт) класу так: MyClass:= TMyClass.Create;
Знищується так: FreeAndNil(MyClass);
або так: MyClass.Free;
|
Класи в мові Java |
---|
Визначення класу на мові Java за допомогою оператора class MyClass {
String name = "Example";
// "Конструктор"
public MyClass(String name) {
this.name = name;
}
// "Метод"
public String getName() {
return name;
}
}
Створення екземпляра класу: MyClass my = new MyClass("Example 2");
Знищення екземпляра класу відбувається за допомогою «збирача сміття» автоматично. |
Класи в мові C++ |
---|
Клас у мові C++ створюється таким чином: class MyClass: public ParentClass // ParentClass — клас-предок, якщо такий є
{
public:
// елементи в цій секції доступні з будь-якої частини програми
MyClass(); // конструктор
~MyClass(); // деструктор
protected:
// елементи в цій секції доступні з класу і його нащадків
private:
// елементи в цій секції доступні лише з класу; це область доступу за умовчанням
};
Після свого створення клас вважається повноцінним типом даних і, отже екземпляри класу створюються таким чином: MyClass myinstance;
Звернення до членів класу: myinstance.classmember
Знищується екземпляр класу, як і будь-яка змінна, лише у випадку, якщо функція, в якій він був створений, завершила роботу або якщо була примусово звільнена динамічна пам'ять, виділена під клас. |
Класи в мові C# |
---|
Класи в мові C # визначаються таким чином: public class MyClass
{
//Член, доступний будь-якому класу програми
public int k;
//Член, доступний будь-якому класу в тому ж модулі програми
internal int l;
//Член, доступний будь-якому класу в тому ж модулі програми або лише поточним класу і всім його підкласам в іншому модулі
protected internal int m;
//Член, доступний лише поточним класу і всім його підкласам
protected int n;
//Член, доступний лише з поточного класу (за замовчуванням).
private int p;
}
На відміну від C++ модифікатори доступу повинні вказуватися для кожного члена окремо. Анонімні класи можна визначити в методі, наприклад, так: public void DoSomething()
{
var person = new
{
Name = "Маргарита";
Age = 15;
}
var pet = new
{
Name = "Дуня";
Type = "Черепаха";
Owner = person;
}
Console.WriteLine("Age of pet owner:" + pet.Owner.Age);
}
|