PHP Manual

Принцип інкапсуляції в ЗІЗ

16. 02. 2020

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

Наприклад, якщо ми вирішуємо задачу, як повернути результат 1.6 на основі запиту користувача з виразом (5+3)*(2/(7+3)), то, напевно, ніхто з нас не зможе написати одразу жодної функції чи методу, який вирішує цю задачу.

Порада: Готовий розв'язок такого типу прикладу є у статті Обробка математичного виразу у вигляді рядка, але будьте готові до того, що він не є простим.

Інкапсуляція приносить абстракцію над об'єктами

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

Припустимо, ми маємо справу з розрахунком заробітної плати працівника і хочемо використати для цього існуючий клас іншого програміста. Нам потрібно лише знати обов'язкові параметри конструктора і ми можемо "просто користуватись" класом:

$mzda = new MzdaZamestnance(
25000, // заробітна плата брутто
6, // кількість років в компанії
10, // кількість років досвіду
true // Це чоловік?
);
echo $mzda->getHruba(); // 25000
echo $mzda->getCista(); // 17800

Параметри об'єкту є фіктивними і не відповідають реально тому, як нараховується заробітна плата. Зокрема, цей принцип ілюструється тим, що нам необхідно знати лише загальний публічний інтерфейс і навіть не потрібно мати справу з внутрішнім станом об'єкта, навіть з внутрішньою реалізацією, і вже тим більше з чому він працює так, як він працює. Ми просто викликаємо метод getCista() і отримуємо чистий виграш.

Інкапсуляція - це питання дизайну

Важливо зазначити, що інкапсуляція сама по собі не є особливістю або синтаксисом мови. Те, що клас і додаток інкапсулюється, є лише питанням програміста, який розробляє додаток і думає про код.

Завжди думайте про дизайн класу саме так:

  • KISS (keep it simple), зберігайте інтерфейс простим і не змушуйте користувача думати про щось зайве. Розгадайте складну логіку для користувача і він буде вдячний.
  • Користувачу класу (іншому програмісту або Вам у майбутньому) зовсім не обов'язково знати внутрішню логіку, достатньо лише назв методів та їх параметрів.
  • Якщо для розрахунку потрібні допоміжні обчислення, які не представляють інтересу для користувача і носять виключно технічний характер, то для них взагалі немає сенсу створювати геттер і вони повинні обчислюватися тільки внутрішніми засобами.
  • Клас повинен задовольняти основним властивостям алгоритму, зокрема, щоб він працював в загальному випадку для будь-яких даних.
  • Загальнодоступні методи повинні бути розроблені таким чином, щоб надавати достатньо інформації для легкого розширення об'єкта новими функціями в майбутньому, щоб ми могли легко обчислювати нові дані з того, що ми вже знаємо.

Зберігати внутрішні дані в таємниці

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

Наприклад, нехай у нас є об'єкт, що представляє банківський рахунок, на який ми хочемо проводити платежі і мати справу з поточним балансом:

class BankAccount
{
private int $sum;
public function __construct(int $startSum)
{
$this->sum = $startSum >= 0 ? $startSum : 0;
}
public function getSum(): int
{
return $this->sum;
}
public function pay(int $price): void
{
$newSum = $this->sum - $price;
if ($newSum < 0) {
throw new \Exception('У вас немає таких грошей!');
}
$this->sum = $newSum;
}
public function addMoney(int $money): void
{
$this->sum += $money;
}
}

Зверніть увагу, що клас містить лише одну private властивість $sum, яка містить поточний баланс.

Якщо ми хочемо отримати поточний баланс, то для цього є метод getSum(), але у нас немає можливості змінити нове значення балансу. Ми можемо тільки зняти гроші методом pay() або додати гроші методом addMoney().

Завдяки цьому принципу ми завжди точно знаємо, що ніхто не зможе зламати об'єкт.

Якщо користувач спробує сплатити більше грошей, ніж є насправді на рахунку, то метод pay() не дозволить цього зробити, оскільки перед перезаписом властивості $sum він виконує перевірочний розрахунок і якщо залишок повинен бути від'ємним (меншим за нуль), то генерується виключення помилки і операція припиняється.

Висновок

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

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

Наступного разу ми розглянемо відданість та видимість.

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.
7.
Status:
All systems normal.
2024