Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

Перейти на сайт->

Бесплатный Курс "Практика HTML5 и CSS3"

Освойте бесплатно пошаговый видеокурс

по основам адаптивной верстки

на HTML5 и CSS3 с полного нуля.

Начать->

Фреймворк Bootstrap: быстрая адаптивная вёрстка

Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.

Верстайте на заказ и получайте деньги.

Получить в подарок->

Бесплатный курс "Сайт на WordPress"

Хотите освоить CMS WordPress?

Получите уроки по дизайну и верстке сайта на WordPress.

Научитесь работать с темами и нарезать макет.

Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

Получить в подарок->

*Наведите курсор мыши для приостановки прокрутки.


Управление доступом к классам: спецификаторы доступа public, private и protected

Перед изучением данной статьи вы можете прочитать предыдущую статью из этой серии - "Работа с наследованием в PHP".

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

Элементы класса можно объявить как public (общедоступные), private (закрытые) или protected (защищенные).

К общедоступным свойствам и методам можно получать доступ из любого контекста.

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

К защищенным свойствам и методам можно получить доступ либо из содержащего их класса, либо из его подкласса. Никакому внешнему коду такой доступ не предоставляется.

Чем это может быть нам полезно? Ключевые слова, определяющие область видимости, позволяют показывать только те аспекты класса, которые требуются клиенту. Это позволяет создать ясный и понятный интерфейс для объекта.

Контроль доступа, позволяющий запрещать клиенту доступ к некоторым свойствам, поможет также избежать ошибок в коде. предположим, мы хотим сделать так, чтобы в объектах типа ShopProduct поддерживались скидки. Для этого можно добавить свойство $discount и метод setDiscount().


// Класс ShopProduct...
public $discount = 0;
// ...
function setDiscount( $num ) {
    $this->discount=$num;
    }

Вооруженные механизмом определения скидки, мы можем создать метод getPrice(), который принимает во внимание примененную скидку.


// Класс ShopProduct...
function getPrice() {
    return ($this->price - $this->discount);
    }

Но тут у нас есть проблема. Мы хотим показать всем только скорректированную цену, но клиент может легко обойти метод getPrice() и получить доступ к свойству $price.


print "Цена - {$product1->price}\n";

В результате будет выведена исходная цена, а не цена со скидкой, которую мы хотим представить. Чтобы предотвратить это, можно просто закрыть свойство $price. Это позволит запретить клиентам прямой доступ к нему, заставляя использовать метод getPrice().

Любая попытка получить доступ к свойству $price из-за пределов класса ShopProduct закончится неудачей. В результате для внешнего мира это свойство прекратит существование.

Но определение свойств как private - не всегда удачная стратегия, поскольку тогда дочерний класс не сможет получить доступ к закрытым свойствам. А теперь представьте, что правила вашего бизнеса таковы: при покупке только книг скидку на них делать нельзя. Мы можем переопределить метод getPrice(), чтобы он возвращал свойство $price без применения скидки.


// Класс BookProduct
function getPrice() {
    return $this->price;
    }

Поскольку свойство $price объявлено в классе ShopProduct, а не в BookProduct, попытка в приведенном выше коде получить к нему доступ закончится неудачей.

Чтобы решить эту проблему, нужно объявить свойство $price защищенным (protected) и тем самым предоставить доступ к нему дочерним классам. Помните, что к защищенному свойству или методу нельзя получить доступ из-за пределов иерархии того класса, в котором это свойство или метод были объявлены. Доступ к ним можно получить только из исходного класса или его дочерних классов.

Как правило, появление ошибок при доступе к свойствам или методам способствует созданию хорошо защищенного кода. Сначала сделайте свойства закрытыми или защищенными, а затем ослабляйте ограничения по мере необходимости.

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


Методы как средство доступа к свойствам

Даже если в клиентской программе нужно будет работать со значениями, хранящимися в экземпляре вашего класса, как правило, стоит запретить прямой доступ к свойствам этого объекта. Вместо этого создайте методы, которые возвращают или устанавливают нужные значения. Такие методы называют методами доступа (accessor) или получателями (getter) и установщиками (setter).

Вы уже видели одно преимущество, которое дают методы доступа: их можно использовать для фильтрации значений свойств в зависимости от обстоятельств, как было проиллюстрировано выше с помощью метода getPrice().

Метод-установщик может также использоваться для принудительного определения типа свойства. Мы уже видели, что для ограничения типа аргументов метода можно использовать уточнения, но у нас нет непосредственного контроля над типами свойств. Помните определение класса ShopProductWriter, с помощью которого выводилась информация об объектах типа ShpoProduct?

Давайте попробуем пойти дальше и сделать так, чтобы класс ShopProductWriter мог выводить информацию о любом количестве объектов типа ShopProduct одновременно.


class ShopProductWriter {
    private $products = array();

    public function addProduct( ShopProduct $shopProduct ) {
        $this->products[]=$shopProduct;
    }

    public function write() {
        $str = "";
        foreach ( $this->products as $shopProduct ) {
            $str .= "{$shopProduct->title}: " .
                    $shopProduct->getProducer() .
                    " ({$shopProduct->getPrice()})\n";
        }
        print $str;
    }
}

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

Хотя мы предоставили метод addProduct(), мы не запретили программистам непосредственно выполнять операции над свойством $products. В результате можно не только добавить объект неправильного типа к массиву свойств $products, но и затереть весь массив и заменить его значением элементарного типа. Чтобы не допустить этого, нужно сделать свойство $products закрытым.

class ShopProductWriter {
    private $products = array();
    //...

Теперь внешний код не сможет повредить массив свойств $products. Весь доступ к нему должен осуществляться через метод addProduct(), а уточнения типа класса, которые используются в объявлении этого метода, гарантируют, что к массиву свойств могут быть добавлены только объекты типа ShopProduct.


Семейство классов ShopProduct

И в заключение данного материала давайте изменим класс ShopProduct и его дочерние классы так, чтобы ограничить доступ к свойствам.


class ShopProduct {
    private $title;
    private $producerMainName;
    private $producerFirstName;
    protected $price;
    private $discount = 0;

    public function __construct(   $title, $firstName,
                            $mainName, $price ) {
        $this->title             = $title;
        $this->producerFirstName = $firstName;
        $this->producerMainName  = $mainName;
        $this->price             = $price;
    }

    public function getProducerFirstName() {
        return $this->producerFirstName;
    }

    public function getProducerMainName() {
        return $this->producerMainName;
    }

    public function setDiscount( $num ) {
        $this->discount=$num;
    }

    public function getDiscount() {
        return $this->discount;
    }

    public function getTitle() {
        return $this->title;
    }

    public function getPrice() {
        return ($this->price - $this->discount);
    }

    public function getProducer() {
        return "{$this->producerFirstName}".
               " {$this->producerMainName}";
    }

    function getSummaryLine() {
        $base  = "$this->title ( $this->producerMainName, ";
        $base .= "$this->producerFirstName )";
        return $base;
    }
}

class CdProduct extends ShopProduct {
    private $playLength = 0;

    public function __construct(   $title, $firstName,
                            $mainName, $price, $playLength ) {
        parent::__construct(    $title, $firstName,
                                $mainName, $price );
        $this->playLength = $playLength;
    }

    public function getPlayLength() {
        return $this->playLength;
    }

    public function getSummaryLine() {
        $base = parent::getSummaryLine();
        $base .= ": Время звучания - {$this->playLength}";
        return $base;
    }

}

class BookProduct extends ShopProduct {
    private $numPages = 0;

    public function __construct(   $title, $firstName,
                            $mainName, $price, $numPages ) {
        parent::__construct(    $title, $firstName,
                                $mainName, $price );
        $this->numPages = $numPages;
    }

    public function getNumberOfPages() {
        return $this->numPages;
    }

    public function getSummaryLine() {
        $base = parent::getSummaryLine();
        $base .= ": Страниц - {$this->numPages}";
        return $base;
    }

    public function getPrice() {
        return $this->price;
    }
}

В этой версии семейства классов ShopProduct нет ничего существенно нового. Все методы были явно сделаны общедоступными, а все свойства теперь стали либо закрытыми, либо защищенными. И для завершенности мы добавили ряд методов доступа.

Далее мы с вами поговорим про статические методы и свойства.

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Смотрите также:

PHP: Получение информации об объекте или классе, методах, свойствах и наследовании

PHP: Получение информации об объекте или классе, методах, свойствах и наследовании

CodeIgniter: жив или мертв?

CodeIgniter: жив или мертв?

Функции обратного вызова, анонимные функции и механизм замыканий

Функции обратного вызова, анонимные функции и механизм замыканий

Применение функции к каждому элементу массива

Применение функции к каждому элементу массива

Слияние массивов. Преобразование массива в строку

Слияние массивов. Преобразование массива в строку

Деструктор и копирование объектов с помощью метода __clone()

Деструктор и копирование объектов с помощью метода __clone()

Эволюция веб-разработчика или Почему фреймворк - это хорошо?

Эволюция веб-разработчика или Почему фреймворк - это хорошо?

Магические методы в PHP или методы-перехватчики (сеттеры, геттеры и др.)

Магические методы в PHP или методы-перехватчики (сеттеры, геттеры и др.)

PHP: Удаление элементов массива

PHP: Удаление элементов массива

Ключевое слово final (завершенные классы и методы в PHP)

Ключевое слово final (завершенные классы и методы в PHP)

50 классных сервисов, программ и сайтов для веб-разработчиков

50 классных сервисов, программ и сайтов для веб-разработчиков

Наверх