От автора: до сих пор мы с Вами, при создании методов и свойств, объявляли их как public, то есть абсолютно открытыми, что не совсем хорошо. Поэтому в данной статье, мы рассмотрим оставшиеся два спецификаторы доступа, а также изучим последний основной принцип, который был пропущен, под названием — инкапсуляция в ООП на PHP.
Итак, хотел бы напомнить, что элементы класса, могут быть объявлены, как:
public – открытые, или общедоступные;
protected – защищенные;
private – закрытые.
Доступ к общедоступным свойствам или методам открыт из абсолютно любого контекста. То есть, создав объект интересующего класса, Вы всегда можете считать или изменить значение свойств, объявленный как public, а так же обратиться к методам, у которых определен данный спецификатор доступа.
1 2 3 4 5 6 7 8 |
<?php class Page { public $title = 'Index Page'; } $index = new Page(); echo $index->title; |
По большому счету мы с Вами это уже рассматривали и не однократно использовали на практике, но повторение, как Вы знаете, ни когда не бывает лишним.
К защищенным элементам класса доступ извне, то есть из объекта, получить нельзя. Разрешается обращаться к указанным свойствам или методам из класса, в котором они определены, либо из его подклассов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php class Page { protected $title = 'Index Page'; public function getTitle() { return $this->title; } } $index = new Page(); //приведет к ошибке echo $index->title; |
Таким образом, если мы в предыдущем примере, объявим свойство $title, используя спецификатор доступа protected — попытка обращения к нему, через объект, незамедлительно приведет к ошибке. Но при этом, мы знаем, что можно обращаться к подобному свойству, из класса, в котором оно определено, а значит, никто не запрещает определить открытый метод, который будет возвращать значение интересующего свойства.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php class Page { protected $title = 'Index Page'; public function getTitle() { return $this->title; } } $index = new Page(); echo $index->getTitle(); |
Точно также, можно определить метод, который будет изменять значение защищенного свойства.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php class Page { protected $title = 'Index Page'; public function getTitle() { return $this->title; } public function setTitle($title) { $this->title = $title; } } $index = new Page(); $index->setTitle("New String"); echo $index->getTitle(); |
Защищенные свойства и методы, так же доступны и в классах наследниках.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Page { protected $title = 'Index Page'; } class IndexPage extends Page { public function getTitle() { return $this->title; } public function setTitle($title) { $this->title = $title; } } $index = new Page(); $index->setTitle("New String"); echo $index->getTitle(); |
При этом к закрытым свойствам или методам можно получить доступ, только из того класса в котором они определены и не более. Таким образом, если в последнем примере, в родительском классе объявить свойство $title, со спецификатором доступа private – интерпретатор PHP вернет ошибку, так как, повторюсь, из дочернего класса нельзя обращаться к закрытым свойствам.
Конечно же, возникает вопрос, зачем все это нужно? Смотрите, спецификаторы доступа, или как их еще называют, ключевые слова определяющие область видимости, позволяют предоставлять доступ только к тем элементам класса, которые необходимы для конкретного клиента. Соответственно контроль доступа, благодаря которому можно запретить доступ к некоторым элементам класса, помогает избежать не правильной, работы приложения в целом.
Теперь давайте рассмотрим следующий пример. Предположим, что разрабатывается интернет магазин, а значит работать необходимо с товарами, для которых определяется цена, при этом, как правило, механизм ценообразования достаточно сложен, но мы его упростим и условимся, что окончательная цена товара, будет формироваться из некого значения базовой цены, а так же с учетом ставки налогообложения. Поэтому в самом простом виде, создадим следующий класс.
1 2 3 4 5 6 7 8 9 10 11 12 |
class Tovar { public $nalog; public $price; public function setNalog($nalog) { $this->nalog = $nalog; } public function getPriceTovar() { return $this->price + $this->nalog; } } |
Данный класс это как бы основа, которая была создана, одним определенным человеком и далее используя его, другие члены команды будут формировать функционал будущего приложения. При этом класс определяет метод getPriceTovar(), как доступ к окончательной цене товара. Соответственно, что бы получить вышеуказанную цену необходимо сделать следующее:
1 2 3 4 |
$computer = new Tovar(); $computer->setNalog(100); echo $computer->getPriceTovar(); |
Как Вы видите, в принципе все очень просто, есть цена, и есть метод, который ее формирует. Но есть небольшая проблема, которая заключается в том, что можно обратиться напрямую к свойству $price, в том случае если оно объявлено как открытое. И в этом случае, налог не будет учитываться для формирования окончательной цены, а значит, приложение будет работать не корректно. Что бы избежать этого, необходимо объявить свойство $price, со спецификатором доступа private, тем самым доступ к свойству, будет разрешен, только из метода getPriceTovar().
Но, в тоже время, уровень видимости private, так же запрещает доступ к свойству из дочерних классов, что приводит к тому, что мы не сможем создать подкласс, и переопределить метод getPriceTovar(), для несколько иной манипуляции над свойством $price. К примеру, если нужно для некоторой группы товаров, вообще не использовать налог при формировании цены, то мы могли бы переопределить метод getPriceTovar():
1 2 3 4 5 |
class Opt extends Tovar { public function getPriceTovar() { return $this->price; } } |
Но данный код приведет к ошибке, потому как свойство $price, закрыто для всех дочерних классов.
А значит, объявляя данное свойство в родительском классе, более правильно, использовать спецификатор доступа protected и тем самым предоставить доступ к нему дочерним классам.
Из этого вытекает небольшая рекомендация по проектированию структуры класса. Сначала определите все свойства и методы как закрытые, а далее по мере написания кода и необходимости ослабляйте ограничения. Лучше лишний раз закрыть элемент класса, чем ошибочно предоставить больший уровень видимости, чем это необходимо.
Теперь мы с Вами плавно подошли к последнему ключевому понятию, под названием — инкапсуляция.
В объектно-ориентированном программировании на PHP инкапсуляцией называют свойство классов скрывать собственную реализацию. И, по сути, в простейшем виде, мы с Вами только что инкапсулировали данные, используя спецификаторы доступа protected и private и тем самым закрывали, или лучше сказать скрывали свойство $price от клиентского кода. То есть, при этом задается некий интерфейс доступа к данным и по большому счету, зная его, нам абсолютно все равно как устроен конкретный класс, так как мы знаем какие методы в нашем распоряжении и как их необходимо использовать, тем самым предотвращается некорректная работа с данными класса.
При этом полиморфизм, предполагает и реализует несколько другую инкапсуляцию в объектно-ориентированном программировании на PHP и означает, то что, размещая различные реализации определенного интерфейса, Вы скрываете работающий на его основе код. Таким образом, можно создавать новые классы, или редактировать существующие и это не приведет к ошибкам. Значение имеет только интерфейс, а не конкретный механизм который работает на его основе.
На этом данная статья завершена. Всего Вам доброго и удачного кодирования!!!
Комментарии (2)