PHP Manual

Спадковість та видимість у ЗІЗ

16. 02. 2020

Одними з фундаментальних властивостей об'єктно-орієнтованого програмування є **спадкування** та інкапсуляція. Завдяки цим функціям ви зможете легко будувати складну логіку додатків, зберігаючи при цьому хорошу читабельність реалізації.

Принцип успадкування

Успадкування виражається в тому, що реалізація одного класу базується на іншому. В термінології ООП ми говоримо про нащадка (клас, який успадковує) та предка (клас, який ми успадковуємо).

Загалом, успадкування працює так, що нащадок отримує всі ознаки предка, або переймаючи їх точно такими, якими вони були у предка, або видозмінюючи їх на свій лад, або повністю перекриваючи їх і використовуючи власну реалізацію.

Застосування цього підходу дуже широке, і успадкування використовується низкою паттернів дизайну.

Реальне використання спадщини - доповідачі у програмі

Спадкування добре підходить для проектування так званих представників, які є особливим видом класів, що представляють логіку зв'язку в патерні проектування MVC.

Наприклад, нехай у нас є тріо сторінок "Головна", "Контакти" та "Вхід".

У міру реалізації кожної сторінки значна частина логіки буде повторюватися (наприклад, прийняття запиту, побудова URL-адреси, рендеринг шаблону і відправка отриманого HTML). Тому зручно реалізувати єдиного предка з такою логікою і просто використовувати його в нащадках.

Почнемо з того, що спочатку визначимо предка (ім'я класу не має значення, я використовую угоду з фреймворку Nette):

abstract class BasePresenter
{
public function link(string $route, array $params = []): string
{
// реалізація методу побудови URL-адреси
}
public function renderTemplate(string $path, array $params = []): string
{
// логіка рендерингу шаблону
}
}

При визначенні класу я використав нове ключове слово abstract, яке говорить про те, що клас BasePresenter є абстрактним. Це означає, що ми не можемо створити його екземпляр, а тільки використовувати його так, щоб інший клас успадкував і реалізував його. Абстракція має й інші корисні переваги, про які ми поговоримо пізніше. Клас не обов'язково повинен бути абстрактним, щоб бути успадкованим - це лише один з можливих варіантів.

Тепер ми можемо реалізувати другий клас, наприклад HomepagePresenter:

final class HomepagePresenter extends BasePresenter
{
public function run(): void
{
// логіка рендерингу
$this->renderTemplate('домашня сторінка', [
'contactLink' => $this->link('Контакт:за замовчуванням'),
]);
}
}

Тепер у вас є робочий клас HomepagePresenter. Зверніть увагу, що клас є фінальним, а це означає, що його вже не можна успадковувати, що гарантує використання методів саме так, як ми їх задали.

Коли ми реалізовували клас, ми створили новий метод run(), який може обробляти тільки HomepagePresenter. Всередині методу викликаємо метод renderTemplate() та link(), які клас не містить. Однак це не має значення, оскільки ключове слово extends вказує нам, звідки повинні бути успадковані методи, тому вони використовуються.

Завдяки успадкуванню ми змогли досягти багаторазового використання коду, оскільки після написання методи можуть бути використані в різних місцях.

Перевизначення реалізації конкретного методу

Дуже часто буває корисно перевизначити поведінку того чи іншого методу при успадкуванні. Наприклад, якби ми захотіли змінити поведінку методу link() з попереднього прикладу в ContactPresenter, то це виглядало б так:

final class ContactPresenter extends BasePresenter
{
public function run(): void
{
// логіка рендерингу
echo $this->link('Головна сторінка:за замовчуванням', []);
}
public function link(string $route, array $params = []): string
{
return 'https://baraja.cz';
}
}

Щоб перевизначити реалізацію, достатньо знову визначити метод у дочірньому класі та перевизначити тіло методу. Головне, щоб відповідати інтерфейсу та реалізовувати однакові вхідні аргументи.

Видимість спадщини

Іноді деякі методи хотілося б приховати при успадкуванні і використовувати тільки для внутрішнього користування. Або ж дозволити їх використання лише під час успадкування, а не в якості публічного інтерфейсу.

Загалом, таким чином, є кілька простих правил видимості. Ми позначаємо методи як "публічні", "захищені" або "приватні", а правила видимості є наступними:

  • Будь-хто може викликати "загальнодоступні" методи з будь-якого місця, тобто при створенні екземпляру як предка, так і нащадка.
  • Тільки предок або нащадок можуть викликати "захищені" методи, але вони не можуть бути викликані з публічного інтерфейсу при створенні екземпляру. Це внутрішні методи для досягнення успадкування (хороший приклад використання - метод link() у попередньому прикладі).
  • Тільки поточний клас може викликати "приватні" методи, незалежно від успадкування та налаштувань публічного інтерфейсу.

Зміна видимості під час виконання

У дуже специфічних випадках може бути корисно змінити видимість методу під час виконання, а потім викликати його. Цим користуються, наприклад, різні бібліотеки Доктрини.

Для зміни видимості ми потім використовуємо нативний клас ReflectionClass, реалізований самим PHP.

Jan Barášek   Více o autorovi

Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.

Rád vám pomůžu:

Související články

1.
9.
Status:
All systems normal.
2024