Skip to content

Latest commit

 

History

History
81 lines (60 loc) · 7.69 KB

encapsulation.md

File metadata and controls

81 lines (60 loc) · 7.69 KB

Инкапсуляция в ООП

Инкапсуляция - это сокрытие внутреннего устройства и логики работы класса от кода снаружи. "Сокрытие" здесь значит, что код снаружи класса не может напрямую обращаться к полям объекта, а может делать что-то с объектом только через вызов его публичных методов.

Сокрытие реализуется за счет того, что мы ставим всем полям и методам модификаторы доступа protected или private, и только для тех методов, которые должны быть доступны снаружи, ставим public. Что такое модификаторы доступа, описано в мануале PHP.

"Сокрытие" никак не скрывает код от разработчика, но оно позволяет задать правила работы с классом. Инкапсуляция дает такие плюсы:

  • разработчик в методах контролирует данные, передаваемые снаружи, и не позволяет записать недопустимые значения в поля объекта. Например, он может не позволить задать отрицательное количество товара на складе в объекте товара. Таким образом, разработчик может гарантировать корректную работу класса независимо от правильности остального кода.
  • разработчик класса определяет, что можно делать с объектом, а что нельзя.
  • если мы хотим узнать, что записывается в то или иное поле, нам достаточно изучить код одного класса, и не надо изучать весь код приложения.
  • мы можем поменять внутреннюю логику работы класса, не меняя публичные методы, и весь остальной код, который их использует. А если бы код снаружи напрямую работал с полями, то нам пришлось бы искать и исправлять все такие места.
  • упрощается понимание кода: чтобы понять, как использовать класс, не надо читать и разбирать весь его код, достаточно прочитать названия публичных методов (и, может быть, комментарии к ним).

Инкапсуляция помогает реализовать принцип единой ответственности (single responsibility), о том, что каждый класс занимается своей задачей, у каждого класса есть своя зона ответственности, и никто другой не должен в нее лезть.

Если проводить аналогии, то можно представить кофе-машину. Ты нажимаешь кнопку (вызываешь публичный метод) и получаешь кофе (результат вызова этого метода), при этом ты не видишь, что происходит внутри нее и тебе не надо в этом разбираться.

Пример PHP кода с инкапсуляцией

Вот пример кода класса с использованием инкапсуляции:

/** 
 * Объект представляет собой ломаную линию из нескольких сегментов.
 * Показаны только публичные методы, остальное скрыто.
 */
class PolyLine 
{
    // Массив со списком точек
    private $points = [];

    /**
     * Создает новую линию, состоящую из одной начальной точки.
     */
    public function __construct(float $x, float $y) 
    { 
        $this->points[] = [$x, $y];
    }

    /** 
     * Добавляет еще одну точку к ломаной.
     */
    public function addPoint(float $x, float $y): void 
    {  
        $this->points[] = [$x, $y];
    }

    /** 
     * Считает общую длину линии.
     */
    public function calculateLength(): float 
    { 
        // Оставим написание этого метода как упражнение читателю
    }
}

В нем всего 3 публичных метода, включая конструктор, и мы видим, что с объектом можно сделать только три действия:

  • создать ломаную, указав начальную точку
  • добавить к ломаной еще одну точку
  • посчитать длину ломаной как сумму длин отдельных сегментов, длина одного сегмента находится по формуле длины отрезка)

При этом код может проверять передаваемые значения, например, не разрешать 2 раза добавлять одну и ту же точку. Также он не позволяет передать в качестве координат что-либо, кроме чисел. Нам даже не требуется изучать код полностью, чтобы понять, как с ним работать - достаточно глянуть на заголовки публичных методов. Вот пример его использования:

$line = new PolyLine(1, 1);
$line->addPoint(2, 2);
$line->addPoint(4, 7);
echo $line->calculateLength();

Благодаря инкапсуляции мы можем поменять внутреннюю логику класса, например, хранить в массиве $points не массивы из 2 координат, а объекты Point, при этом менять код, работающий с классом, не потребуется.

Замечу, что с помощью расширения Reflection можно обойти инкапсуляцию. Например, библиотека ORM Doctrine использует его, чтобы записывать значения из базы данных в приватные поля объекта. Reflection стоит использовать вдумчиво и в исключительных случаях.

Инкапсуляция за пределами ООП

Инкапсуляция возможна не только в объектно-ориентированном программировании. Например, модули в языках вроде Javascript или Python позволяют скрывать внутреннее устройство и делать доступными только отдельные функции и таким образом тоже реализуют принцип инкапсуляции.