Чат веб-разработчиков
Инкапсуляция в PHP

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

В этом уроке мы узнаем о первом из трёх китов ООП - инкапсуляции. Инкапсуляция (лат. in capsula; от capsula «коробочка») — размещение в оболочке, изоляция, закрытие чего-либо с целью исключения влияния на окружающее. О том, как это используется в объектно-ориентированном программировании, вы узнаете по ходу этого урока.

Свойства объектов

Вернёмся к нашей прошлой теме. Те, кто видел котиков, знают, что некоторые признаки у котиков отличаются: цвет, вес, громкость мяуканья и т.д. Такие признаки есть у всех объектов, в том числе и в наших. И в ООП они называются свойствами объектов. Давайте приведем примеры таких свойств для котиков:

  • имя;
  • цвет;
  • вес.

Давайте теперь создадим более похожий на реального котика класс:

Код доступен только после покупки курса ООП в PHP.

Всё это: $name, $color, $weight - свойства будущих объектов этого класса. Перед именем свойства всегда ставится модификатор доступа. В нашем случае - это public. Это слово говорит о том, что данное свойство будет доступно всем, кто работает с объектами данного класса. Есть и другие модификаторы доступа, но о них чуть ниже.

И снова сам по себе этот код сейчас ничего не выведет. Это опять - просто шаблон.

Итак, мы сделали некоторый шаблон, который вполне себе описывает котиков. Давайте теперь создадим новый объект этого класса.

Код доступен только после покупки курса ООП в PHP.

Так мы создали объект с типом Cat и вывели его с помощью var_dump().

Код доступен только после покупки курса ООП в PHP.

Как видим, в переменной лежит объект (object), и у него есть три свойства, и у всех значения - null. Давайте это исправим. Дадим нашему котику имя, покрасим и наполним его чем-нибудь, чтобы он сколько-нибудь весил. Делается это так:

Код доступен только после покупки курса ООП в PHP.

Оператор -> (стрелочка, состоящая из двух знаков - "тире" и "больше") используется для доступа к свойствам объекта. В данном коде мы обратились к каждому свойству отдельно и присвоили им значения. Если теперь мы выведем $cat1 с помощью var_dump(), то получим следующее:

Код доступен только после покупки курса ООП в PHP.

Как видим, это уже не ерунда какая-то, а белый Снежок, который весит три с половиной кило.

Теперь мы можем обратиться к свойству этого кота и узнать его имя.

Код доступен только после покупки курса ООП в PHP.

И получим в результате "Снежок".

Можем создать несколько котов и задать им разные свойства:

Код доступен только после покупки курса ООП в PHP.

Результат получится вполне ожидаемый:

Код доступен только после покупки курса ООП в PHP.

Два разных объекта со своими значениями свойств.

Это довольно похоже на работу с массивами, как будто записываем значение по ключу.

То, что внутри объектов есть свойства - это уже проявление инкапсуляции. У объекта есть свойства, он их внутри себя содержит - вот и "капсула".

Методы объектов

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

Методы объявляются следующим образом:

Код доступен только после покупки курса ООП в PHP.

public - модификатор доступа к методу, говорящий о том, что его могут вызвать все, кто пользуется объектом, sayHello - имя метода, а далее идут аргументы (в нашем случае их нет), а далее - тело метода, в котором мы просто выводим строку 'Мяу!';

Как мы видим, в целом методы объектов не сильно отличаются от обычных функций. При их описании мы только добавляем модификатор доступа.

Вызвать метод мы можем у созданного объекта. Давайте создадим нового кота и попросим его с нами поздороваться. Для вызова метода объекта используется такой же оператор как и для доступа к свойствам объекта ->

Код доступен только после покупки курса ООП в PHP.

Этот код выведет строку 'Мяу!'. Вот так вот, с нами поздоровался виртуальный кот!

Переменная $this

Да только методы - это не такие уж и простые функции. Внутри методов доступна специальная переменная $this, и в ней хранится... наш текущий созданный объект. БДЫЩЬ! Мозг взорвался :)

На деле всё не так уж и сложно. Мы можем с помощью этой переменной обращаться к другим методам и свойствам данного объекта. Например, давайте научим кота здороваться по-человечески. Пусть он будет называть своё имя. Для этого нам нужно переписать метод sayHello() следующим образом:

Код доступен только после покупки курса ООП в PHP.

И теперь, когда мы создадим новый объект кота, и попросим его с нами поздороваться, то $this->name вернёт значение свойства name у текущего объекта.

Код доступен только после покупки курса ООП в PHP.

Данный код выведет следующее:

Код доступен только после покупки курса ООП в PHP.

Вот так всё просто. Надеюсь, этот наглядный пример помог вам понять, что $this - это просто текущий объект, и что $this есть только у созданного объекта.

И методы, и переменная $this - тоже инкапсуляция! Но и на этом ещё не всё :)

Модификаторы доступа

Сейчас у нас с вами все свойства и методы объектов являются публичными - из любого места в коде, где этот объект доступен, мы можем получить доступ к этим свойствам и методам. Для того чтобы сделать свойство или метод публичным используется ключевое слово public.

Однако, есть и другие модификаторы доступа, и в этом уроке мы с вами изучим ещё один модификатор - private. Он позволяет сделать свойства и методы объекта приватными, после этого они будут доступны только внутри этого объекта.

Например, давайте изменим модификатор для свойства name:

Код доступен только после покупки курса ООП в PHP.

Давайте теперь попытаемся изменить это свойство у объекта:

Код доступен только после покупки курса ООП в PHP.

Мы получим ошибку:
Ошибка при попытке доступа к приватному свойству

Однако, мы можем написать публичный метод, который позволит задать данное свойство с помощью него. Назовём его setName(). Он будет брать переданную в него строку и устанавливать это значение в свойство name.

Код доступен только после покупки курса ООП в PHP.

Теперь давайте зададим имя коту с помощью этого метода:

Код доступен только после покупки курса ООП в PHP.

Теперь всё успешно отработало, и кот даже сказал своё имя с помощью метода sayHello(). Однако если бы мы попытались просто вывести его имя вот так:

Код доступен только после покупки курса ООП в PHP.

то мы бы снова получили ошибку доступа.

Как вы могли заметить с помощью метода setName мы позволяем задать в свойство name только строку. Ни число, ни массив, ни что-то ещё, а именно - строку. Это хороший подход - это позволяет использовать различного рода валидации. Ведь внутри метода вы можете выполнить какие-то проверки, прежде чем положить значение в свойство. Это позволяет избежать ситуаций, когда в свойства можно засунуть любую ерунду. Такие методы как setName(), задающие значения свойствам объекта называются сеттерами.

Чтобы получить напрямую значение приватного свойства у объекта можно написать другой публичный метод, который будет просто возвращать значение этого свойства. Напишем метод getName().

Код доступен только после покупки курса ООП в PHP.

Теперь мы можем просто получить имя кота извне:

Код доступен только после покупки курса ООП в PHP.

Такие методы, в свою очередь, именуются геттерами.

Модификаторы доступа - это ещё одно проявление инкапсуляции.

Конструктор

А теперь давайте возьмём и сломаем одного кота :)

Для этого мы не будем давать ему имя, и вызовем метод getName().

Код доступен только после покупки курса ООП в PHP.

Что произойдёт? Правильно - ошибка!

Ведь мы описали, что getName() всегда должна отдавать строку. А в нашем объекте возвращается null.

Можно ли как-то гарантировать, что в свойстве name всегда будет строка? Можно. Для этого существует конструктор - это метод, который вызывается при создании объекта этого класса. В принципе, это такой же метод, как и все другие, он может иметь различные аргументы. Но он обязательно вызывается автоматически при создании объекта класса, в котором он описан.

Метод-конструктор должен называться __construct. Именно так и никак иначе.

Давайте создадим конструктор для нашего кота, который будет иметь обязательный аргумент $name с типом строка.

Код доступен только после покупки курса ООП в PHP.

Конструктор принято объявлять в начале класса, после объявления свойств, но перед другими методами.

Теперь чтобы создать кота с именем Снежок мы должны передать аргумент при создании нового объекта:

Код доступен только после покупки курса ООП в PHP.

И вот что сейчас произошло: аргумент, переданный в круглые скобки, был передан в метод __construct(). Там это значение установилось в свойство объекта name.

Если мы сейчас попробуем узнать имя этого кота, то мы его получим.

Код доступен только после покупки курса ООП в PHP.

А давайте теперь мы попробуем по-старинке создать кота без имени, не передавая аргументов при создании объекта.

Код доступен только после покупки курса ООП в PHP.

Мы получим ошибку.
Ошибка об отсутствии нужного количества аргументов конструктора

Здесь написано, что в конструкторе ожидается 1 обязательный аргумент, а мы не передали ни одного.

Таким образом, мы получили класс котов, объекты которого нельзя создать без имени. И именно так и должно быть в мире - у всех котиков должны быть имена. Согласитесь, такой мир был бы гораздо лучше чем тот, в котором мы живём. Ну так вот в программировании, мы способны построить такие миры, какие сами пожелаем. И это круто :)

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

Это был первый кит ООП - инкапсуляция. До встречи на следующем уроке, а пока за домашку.

Читайте также
Комментарии


SlipKnot
SlipKnot

Домашка:

<?php

class Cat
{
    private $name;
    private $color;
    public $weight;
    public function __construct (string $name, string $color) /*теперь при создании нового кота
                                                                мы должны указать имя и цвет*/
    {
        $this->name = $name;
        $this->color = $color;
    }
    public function sayHello()
    {
    echo 'Дратути, меня зовут ' . $this->name . ', моя шерстка цвета ' . $this->color . '<br>';
    } /* дополнение к методу приветствия */
    public function setName(string $name)
    {
        $this-> name = $name;
    }
    public function setColor(string $color) //метод для присвоения цвета шерсти кота
    {
        $this-> color = $color;
    }
    public function  getName():string
    {
        return $this->name;
    }
    public function getColor():string // получаем значения цвета шерсти
    {
        return $this->color;
    }
}
$cat1 = new Cat('Снежок', 'белый');
$cat1-> sayHello(); //Дратути, меня зовут Снег, моя шерстка цвета белый
$cat1-> setName('Снег');
$cat1->setColor('серый');
$cat2 = new Cat ('Барсик', 'рыжий');
$cat2->sayHello(); //Дратути, меня зовут Барсик, моя шерстка цвета рыжий
$cat1->sayHello(); //Дратути, меня зовут Снег, моя шерстка цвета серый
ivashkevich
ivashkevich

Отлично! Всё-таки перекрасили :D

Galay
Galay
<?php
class Cat 
    {
        private $name;
        private $color;
        public function __construct(string $name, string $color){
            $this->name = $name;
            $this->color = $color;
        }

        public function sayHello() {
            echo "Привет, меня зовут {$this->name}, мой цвет - $this->color.";
        }
        public function getColor(): string {
            return $this->color;
        }
        public function setColor(string $color) {
             $this->color = $color;
        }

    }
    $cat1 = new Cat('Барсик','рыжий');
    // $cat1 -> name = 'Барсик';
    // $cat1 -> color = 'рыжий';
    echo '<br>';
    echo $cat1->sayHello();
    $cat2 = new Cat('Снежок','белый');
    // $cat1 -> name = 'Снежок';
    // $cat1 -> color = 'белый';
    echo '<br>';
    echo $cat2->sayHello();
    echo '<br>';
    $cat1->setColor('черный');
    echo $cat1->getColor();

и еще ругается на public function getColor(): string
Parse error: syntax error, unexpected ':', expecting ';' or '{' in codetester_source.php on line 14
а другой сервер не ругается. Почему?

ivashkevich
ivashkevich

Там где ругается - старая версия PHP.

echo $cat1->sayHello();

Вы тут вызываете echo, и внутри метода вызываете echo. Либо в методе возвращайте строку через return, либо тут не нужно делать echo, и оставить его только внутри метода.

Galay
Galay

Странно, я тоже подумал версия старая, но проверил - 7.2
Вобщем не понятно, ну да ладно.. Про echo понял, действительно. Видимо не серьезная ошибка, интерпритатор не ругается.
Артем, а на ООП мало кто домашку в ответах выкладывает?

ivashkevich
ivashkevich

Да, пока мало кто выкладывает, только-только начали покупать вообще)

g--nokoder
g--nokoder

Домашка


<?php
    Class Cat{
        private $name;
        private $color;
        public $weight;

        public function __construct($name, $color){
            $this->name = $name;
            $this->color = $color;
            return $this;
        }

        public function setName($name){
            $this->name = $name;
        }
        public function getName($name){
            $this->name = $name;
            return $this->name;
        }

        public function getColor($color){
            $this->color = $color;
            return $this->color;
        }
        public function sayHello(){
            echo "Привет! Меня зовут ".$this->name." и мой цвет ".$this->color." и мой вес ".$this->weight."<br>";
        }
    }

    $cat1 = new Cat("Снежок", "белый");
    $cat1->weight = "3 кг";

    $cat2 = new Cat("Барсик", "рыжый");
    $cat2->weight = "3,5 кг";

    echo $cat1->sayHello();
    echo $cat2->sayHello();
?>
ivashkevich
ivashkevich
  1. Перед аргументами методов нужно указывать их тип.
  2. return в конструкторе не нужен.
  3. Геттеры не должны ничего изменять у объекта, в нашем случае даже не должны принимать никаких аргументов.
  4. Свойство weight стоит тоже сделать приватным и задаваться через сеттер, а доступ извне должен обеспечиваться через геттер.
computerix
computerix
<?php
class Cat
{
    private $name;
    private $color;

    public function __construct(string $name, string $color)
    {
        $this->name = $name;
        $this->color = $color;
    }

    public function sayHello()
    {
        echo 'Привет! Меня зовут ' . $this->name . ',' . 'Мой цвет '. $this->color . '.';
    }

    public function setName(string $name)
    {
        $this->name = $name;
    }

    public function setColor(string $color)
    {
        $this->color = $color;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getColor(): string
    {
        return $this->color;
    }
}
$cat1 = new Cat('Снежок','белый');
$cat1->sayHello(); 
$cat2 = new Cat('Барсик','рыжий');
$cat2->sayHello();
?>
ArtemijeKA
ArtemijeKA

Случайно два сообщения написал) ДЗ:

ArtemijeKA
ArtemijeKA

1

public function sayHello()
    {
        echo 'Привет! Меня зовут ' . $this->name . '.';
        echo '<br>';
        echo 'Мой цвет ' . $this->color . ').';
    }

2

public function __construct(string $name, string $color)
    {
        $this->name = $name;
        $this->color = $color;
    }

3

public function getColor(): string // возвращяемое значение будет строкой
    {
        return $this->color;
    }

4

*Не думаю что стоит)*

5

$cat1 = new Cat('Снежок', 'белый');
$cat2 = new Cat('Барсик', 'рыжий');

$cat1->sayHello();
$cat2->sayHello();

6 Спасибо!

jeeree
jeeree
class Cat
{
    public function __construct(string $name, $color, float $weight)
    {
        $this->name = $name;
        $this->color = $color;
        $this->weight = $weight;
    }
    private $name;
    private $color;
    private $weight;

    public function sayHello()
    {
        echo 'Дароу! My name is ' . $this->name . ', i have ' .
            $this->color . ' color and i weight ' . $this->weight . ' kilograms.','<br>';
    }
    public function setName(string $name)
    {
        $this->name = $name;
    }
    public function setColor(string $color)
    {
        $this->color = $color;
    }
    public function setWeight(float $weight)
    {
        $this->weight = $weight;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getColor(): string
    {
        return $this->color;
    }public function getWeight(): float
{
    return $this->weight;
}
}
$cat1 = new Cat('Тупой и пакостный ИШАК', 'white', "5.3");
$cat1->sayHello();
jeeree
jeeree

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

ArtemijeKA
ArtemijeKA

Для того чтобы потом в дальнейшем через эти методы получать и задавать новые значения. Сеттеры как правило содержат в себе всяческие обработчики и валидаторы для новых значений, что бы не Бог весь что туда не засунулось - как-то так вроде. В геттерах тоже данные наверно как-то обрабатывать можно в зависимости от заданных аргументов геттеру.

ivashkevich
ivashkevich

Все верно =)

DmitryGavrilov
DmitryGavrilov
<?php

class Cat {
    private $name;
    private $color;

    public function __construct(string $name, string $color) {

        $this->name = $name;
        $this->color = $color;
    }

    public function SayHello(){
        echo 'Привет! Меня зовут ' . $this->name . '.' . '<br>' . 'мой цвет ' . $this->color . '.';

    }

    public function setName(string $name) {
        $this->name = $name;
    }

    public function setColor (string $color) {
        $this->color = $color;
    }

    public function getName(): string {
        return $this->name;

    }

    public function getColor() : string  {
        return $this->color;
    }
}

$cat1 = new Cat('Маленький пушистый монстр', 'Ультрамарин');
$cat1->setName('Пушистый монстр');
$cat1->setColor('Ультрафиолетовый');
echo  $cat1->SayHello();

?>
ivashkevich
ivashkevich

Супер! Только имена методов всегда с маленькой буквы.

DmitryGavrilov
DmitryGavrilov

Будет сделано Джеди-Мастер

Benya
Benya
<?php

class Cat
{
    private $name;
    private $color;

    public function __construct(string $name, string $color)
    {
        $this->name = $name;
        $this->color = $color;
    }

    public function sayHello()
    {
        echo 'Привет! Меня зовут ' . $this->name . ','  . ' у меня  ' . $this->color . ' цвет' . '<br>';
    }

    public function setName(string $name)
    {
        $this->name = $name;
    }

    public function setColor(string $color)
    {
        $this->color = $color;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getColor(): string
    {
        return $this->color;
    }
}

$cat1 = new Cat('Буся', 'желтый');
$cat1->sayHello();
$cat1 = new Cat('Снежек', 'белый');
$cat1->sayHello();
$cat1 = new Cat('Барсик', 'рыжый');
$cat1->sayHello();
SBTesla
SBTesla
<?php

class Cat {

     private $name;
     private $color;
     private $weight;

     public  function __construct(string $name, string $color, string $weight){
          $this ->name = $name;
          $this -> color = $color;
          $this -> weight = $weight;
     }

     public function sayHello () {
          echo 'Превет! меня зовут ' . $this -> name . ' моя шерстка  ' . $this -> color . ' и  вешу я столько: ' . $this ->weight . '<br>';
     }
     public function  setName(string $name)
     {
          $this ->name = $name;
     }
     public function setColor(string $color)
     {
          $this->color = $color;
     }
     public function setWeight(float $weight)
     {
          $this->weight = $weight;
     }

     public function getName():string {
          return $this -> name;
     }
     public function getColor():string
     {
          return $this-> color;
     }
     public function getWeight():float
     {
          return $this-> weight;
     }

}
// создаем первый обьект
$cat1 = new Cat ('снежок', 'белый', '10');
$cat1->sayHello();
// создаем 2 обьект
$cat2 = new Cat('барсик', 'синий', '20');
$cat2->sayHello();

поправил

ivashkevich
ivashkevich

Потому что название конструктора начинается с двух подчеркиваний. Замечания:

  1. Слишком много очевидных комментариев.
  2. Коммент "передаем в обьект нужные нам параметры" не имеет никакого отношения к коду, над которым он написан
  3. Коммент "пердаем в параметр обьектов строку с свойствами" вообще бессмысленный - у объектов есть свойства, в которые можно что-то присвоить. У них нет никаких параметров.
  4. Коммент "получаем напрямую свойства приватного обьекта" - не бывает никаких приватных объектов! Где вы взяли эти формулировки?
  5. Конструкции вида $x->y не должны иметь никаких пробелов!