Инкапсуляция в PHP
В этом уроке мы узнаем о первом из трёх китов ООП - инкапсуляции. Инкапсуляция (лат. in capsula; от capsula «коробочка») — размещение в оболочке, изоляция, закрытие чего-либо с целью исключения влияния на окружающее. О том, как это используется в объектно-ориентированном программировании, вы узнаете по ходу этого урока.
Свойства объектов
Вернёмся к нашей прошлой теме. Те, кто видел котиков, знают, что некоторые признаки у котиков отличаются: цвет, вес, громкость мяуканья и т.д. Такие признаки есть у всех объектов, в том числе и в наших. И в ООП они называются свойствами объектов. Давайте приведем примеры таких свойств для котиков:
- имя;
- цвет;
- вес.
Давайте теперь создадим более похожий на реального котика класс:
<?php
class Cat
{
public $name;
public $color;
public $weight;
}
Всё это: $name, $color, $weight - свойства будущих объектов этого класса. Перед именем свойства всегда ставится модификатор доступа. В нашем случае - это public. Это слово говорит о том, что данное свойство будет доступно всем, кто работает с объектами данного класса. Есть и другие модификаторы доступа, но о них чуть ниже.
И снова сам по себе этот код сейчас ничего не выведет. Это опять - просто шаблон.
Итак, мы сделали некоторый шаблон, который вполне себе описывает котиков. Давайте теперь создадим новый объект этого класса.
<?php
class Cat
{
public $name;
public $color;
public $weight;
}
$cat1 = new Cat();
var_dump($cat1);
Так мы создали объект с типом Cat и вывели его с помощью var_dump().
object(Cat)[1]
public 'name' => null
public 'color' => null
public 'weight' => null
Как видим, в переменной лежит объект (object), и у него есть три свойства, и у всех значения - null. Давайте это исправим. Дадим нашему котику имя, покрасим и наполним его чем-нибудь, чтобы он сколько-нибудь весил. Делается это так:
...
$cat1->name = 'Снежок';
$cat1->color = 'white';
$cat1->weight = 3.5;
Оператор -> (стрелочка, состоящая из двух знаков - "тире" и "больше") используется для доступа к свойствам объекта. В данном коде мы обратились к каждому свойству отдельно и присвоили им значения. Если теперь мы выведем $cat1 с помощью var_dump(), то получим следующее:
object(Cat)[1]
public 'name' => string 'Снежок' (length=12)
public 'color' => string 'white' (length=5)
public 'weight' => float 3.5
Как видим, это уже не ерунда какая-то, а белый Снежок, который весит три с половиной кило.
Теперь мы можем обратиться к свойству этого кота и узнать его имя.
echo $cat1->name;
И получим в результате "Снежок".
Можем создать несколько котов и задать им разные свойства:
<?php
class Cat
{
public $name;
public $color;
public $weight;
}
$cat1 = new Cat();
$cat1->name = 'Снежок';
$cat1->color = 'white';
$cat1->weight = 3.5;
$cat2 = new Cat();
$cat2->name = 'Барсик';
$cat2->color = 'black';
$cat2->weight = 6.2;
var_dump($cat1);
var_dump($cat2);
Результат получится вполне ожидаемый:
object(Cat)[1]
public 'name' => string 'Снежок' (length=12)
public 'color' => string 'white' (length=5)
public 'weight' => float 3.5
object(Cat)[2]
public 'name' => string 'Барсик' (length=12)
public 'color' => string 'black' (length=5)
public 'weight' => float 6.2
Два разных объекта со своими значениями свойств.
Это довольно похоже на работу с массивами, как будто записываем значение по ключу.
То, что внутри объектов есть свойства - это уже проявление инкапсуляции. У объекта есть свойства, он их внутри себя содержит - вот и "капсула".
Методы объектов
Но одними только свойствами объекты не ограничиваются. Они могут иметь методы - это функции, принадлежащие конкретным объектам. Их мы описываем внутри класса, после чего эти функции становятся связаны с объектами этого класса.
Методы объявляются следующим образом:
class Cat
{
public $name;
public $color;
public $weight;
public function sayHello()
{
echo 'Мяу!';
}
}
public - модификатор доступа к методу, говорящий о том, что его могут вызвать все, кто пользуется объектом, sayHello - имя метода, а далее идут аргументы (в нашем случае их нет), а далее - тело метода, в котором мы просто выводим строку 'Мяу!';
Как мы видим, в целом методы объектов не сильно отличаются от обычных функций. При их описании мы только добавляем модификатор доступа.
Вызвать метод мы можем у созданного объекта. Давайте создадим нового кота и попросим его с нами поздороваться. Для вызова метода объекта используется такой же оператор как и для доступа к свойствам объекта ->
$cat1 = new Cat();
$cat1->name = 'Снежок';
$cat1->color = 'white';
$cat1->weight = 3.5;
$cat1->sayHello();
Этот код выведет строку 'Мяу!'. Вот так вот, с нами поздоровался виртуальный кот!
Переменная $this
Да только методы - это не такие уж и простые функции. Внутри методов доступна специальная переменная $this, и в ней хранится... наш текущий созданный объект. БДЫЩЬ! Мозг взорвался :)
На деле всё не так уж и сложно. Мы можем с помощью этой переменной обращаться к другим методам и свойствам данного объекта. Например, давайте научим кота здороваться по-человечески. Пусть он будет называть своё имя. Для этого нам нужно переписать метод sayHello() следующим образом:
class Cat
{
public $name;
public $color;
public $weight;
public function sayHello()
{
echo 'Привет! Меня зовут ' . $this->name . '.';
}
}
И теперь, когда мы создадим новый объект кота, и попросим его с нами поздороваться, то $this->name вернёт значение свойства name у текущего объекта.
<?php
class Cat
{
public $name;
public $color;
public $weight;
public function sayHello()
{
echo 'Привет! Меня зовут ' . $this->name . '.';
}
}
$cat1 = new Cat();
$cat1->name = 'Снежок';
$cat1->sayHello();
$cat2 = new Cat();
$cat2->name = 'Барсик';
$cat2->sayHello();
Данный код выведет следующее:
Привет! Меня зовут Снежок.
Привет! Меня зовут Барсик.
Вот так всё просто. Надеюсь, этот наглядный пример помог вам понять, что $this - это просто текущий объект, и что $this есть только у созданного объекта.
И методы, и переменная $this - тоже инкапсуляция! Но и на этом ещё не всё :)
Модификаторы доступа
Сейчас у нас с вами все свойства и методы объектов являются публичными - из любого места в коде, где этот объект доступен, мы можем получить доступ к этим свойствам и методам. Для того чтобы сделать свойство или метод публичным используется ключевое слово public.
Однако, есть и другие модификаторы доступа, и в этом уроке мы с вами изучим ещё один модификатор - private. Он позволяет сделать свойства и методы объекта приватными, после этого они будут доступны только внутри этого объекта.
Например, давайте изменим модификатор для свойства name:
class Cat
{
private $name;
public $color;
public $weight;
public function sayHello()
{
echo 'Привет! Меня зовут ' . $this->name . '.';
}
}
Давайте теперь попытаемся изменить это свойство у объекта:
$cat1 = new Cat();
$cat1->name = 'Снежок';
Однако, мы можем написать публичный метод, который позволит задать данное свойство с помощью него. Назовём его setName(). Он будет брать переданную в него строку и устанавливать это значение в свойство name.
class Cat
{
private $name;
public $color;
public $weight;
public function sayHello()
{
echo 'Привет! Меня зовут ' . $this->name . '.';
}
public function setName(string $name)
{
$this->name = $name;
}
}
Теперь давайте зададим имя коту с помощью этого метода:
$cat1 = new Cat();
$cat1->setName('Снежок');
$cat1->sayHello();
Теперь всё успешно отработало, и кот даже сказал своё имя с помощью метода sayHello(). Однако если бы мы попытались просто вывести его имя вот так:
echo $cat1->name;
то мы бы снова получили ошибку доступа.
Как вы могли заметить с помощью метода setName мы позволяем задать в свойство name только строку. Ни число, ни массив, ни что-то ещё, а именно - строку. Это хороший подход - это позволяет использовать различного рода валидации. Ведь внутри метода вы можете выполнить какие-то проверки, прежде чем положить значение в свойство. Это позволяет избежать ситуаций, когда в свойства можно засунуть любую ерунду. Такие методы как setName(), задающие значения свойствам объекта называются сеттерами.
Чтобы получить напрямую значение приватного свойства у объекта можно написать другой публичный метод, который будет просто возвращать значение этого свойства. Напишем метод getName().
class Cat
{
private $name;
public $color;
public $weight;
public function sayHello()
{
echo 'Привет! Меня зовут ' . $this->name . '.';
}
public function setName(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
Теперь мы можем просто получить имя кота извне:
$cat1 = new Cat();
$cat1->setName('Снежок');
echo $cat1->getName();
Такие методы, в свою очередь, именуются геттерами.
Модификаторы доступа - это ещё одно проявление инкапсуляции.
Конструктор
А теперь давайте возьмём и сломаем одного кота :)
Для этого мы не будем давать ему имя, и вызовем метод getName().
$cat1 = new Cat();
echo $cat1->getName();
Что произойдёт? Правильно - ошибка!
Ведь мы описали, что getName() всегда должна отдавать строку. А в нашем объекте возвращается null.
Можно ли как-то гарантировать, что в свойстве name всегда будет строка? Можно. Для этого существует конструктор - это метод, который вызывается при создании объекта этого класса. В принципе, это такой же метод, как и все другие, он может иметь различные аргументы. Но он обязательно вызывается автоматически при создании объекта класса, в котором он описан.
Метод-конструктор должен называться __construct. Именно так и никак иначе.
Давайте создадим конструктор для нашего кота, который будет иметь обязательный аргумент $name с типом строка.
class Cat
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function sayHello()
{
echo 'Привет! Меня зовут ' . $this->name . '.';
}
public function setName(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
Конструктор принято объявлять в начале класса, после объявления свойств, но перед другими методами.
Теперь чтобы создать кота с именем Снежок мы должны передать аргумент при создании нового объекта:
$cat1 = new Cat('Снежок');
И вот что сейчас произошло: аргумент, переданный в круглые скобки, был передан в метод __construct(). Там это значение установилось в свойство объекта name.
Если мы сейчас попробуем узнать имя этого кота, то мы его получим.
echo $cat1->getName();
А давайте теперь мы попробуем по-старинке создать кота без имени, не передавая аргументов при создании объекта.
$cat1 = new Cat();
Здесь написано, что в конструкторе ожидается 1 обязательный аргумент, а мы не передали ни одного.
Таким образом, мы получили класс котов, объекты которого нельзя создать без имени. И именно так и должно быть в мире - у всех котиков должны быть имена. Согласитесь, такой мир был бы гораздо лучше чем тот, в котором мы живём. Ну так вот в программировании, мы способны построить такие миры, какие сами пожелаем. И это круто :)
Подводя итог, можно сказать что инкапсуляция - это возможность объектов содержать в себе свойства и методы. Так мы делаем их зависимыми друг от друга внутри этой "капсулы".
Это был первый кит ООП - инкапсуляция. До встречи на следующем уроке, а пока за домашку.
Комментарии