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

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

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

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

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

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

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

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

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

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

Начать->

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

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

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

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

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

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

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

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

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

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

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

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


Позднее статическое связывание: ключевое слово static

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


После того, как мы рассмотрели абстрактные классы и интерфейсы, самое время снова обратиться к статическим методам.

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


abstract class DomainObject {

}

class User extends DomainObject {
    public static function create() {
        return new User();
    }
}

class Document extends DomainObject {
    public static function create() {
        return new Document();
    }
}

Сначала я создал суперкласс под именем DomainObject. Само собой разумеется, что в реальном проекте в нем будет находиться функциональность, общая для всех дочерних классов.

После этого я создал два дочерних класса User и Document. Я хотел, чтобы в каждом из моих конкретных классов находился метод create().

* Примечание
Почему для создания конкретного объекта я воспользовался статическим методом-фабрикой, а не оператором new и конструктором объекта?

Дело в том, что существует шаблон под названием Identity Map (материал по нему скоро будет доступен) и он создает и инициализирует новый объект только в том случае, если объект с аналогичными отличительными особенностями еще не создан. Если таковой объект существует, то возвращается просто ссылка на него.

Статический метод-фабрика наподобие рассмотренного выше метода create() является отличными кандидатом для реализации подобной функциональности.

Созданный мною код прекрасно работает, но в нем есть досадный недостаток - дублирование. Мне совсем не нравится повторять однотипный код наподобие того, что приведен выше, для каждого создаваемого дочернего объекта, расширяющего класс DomainObject. Как насчет того, чтобы переместить метод create() в суперкласс?


abstract class DomainObject {
    public static function create() {
        return new self();
    }
}

class User extends DomainObject {
}

class Document extends DomainObject {
}

Document::create();

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

Однако насчет ключевого слова self я сделал допущение, что оно должно так работать. На самом деле оно не работает для классов так же, как псевдопеременная $this для объектов.

С помощью ключевого слова self нельзя сослаться на вызывающий объект. Оно используется только для разрешения ссылок на содержащий класс, в контексте которого вызывается метод. Поэтому при попытке запуска приведенного выше примера получим следующее сообщение об ошибке:


PHP Fatal error: Cannot instantiate abstract class DomainObject in...

Переводится это так: невозможно создать экземпляр абстрактного класса.

Таким образом, ключевое слово self трансформируется в ссылку на класс DomainObject, в котором определен метод create(), а не на класс Document, для которого этот метод должен быть вызван.

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

В PHP 5.3 впервые введена концепция позднего статического связывания (late static bindings). Самым заметным ее проявлением является введение нового (в данном контексте) ключевого слова static. Оно аналогично ключевому слову self, за исключением того, что относится к вызывающему, а не содержащему классу.

Итак, теперь я смогу воспользоваться всеми преимуществами наследования в статическом контексте.


abstract class DomainObject {
    public static function create() {
        return new static();
    }
}

class User extends DomainObject {
}

class Document extends DomainObject {
}

print_r(Document::create());

В результате будет выведено следующее:


Document Object
(
)

Ключевое слово static можно использовать не только для создания объектов. Так же, как и self и parent, его можно использовать как идентификатор для вызова статических методов даже из нестатического контекста.

Например, я хочу реализовать идею группировки моих классов DomainObject. По умолчанию все классы попадают в категорию "default". Но для некоторых веток иерархии наследования моих классов мне нужно это переопределить.


abstract class DomainObject {
    private $group;
    public function __construct() {
        $this->group = static::getGroup();
    }

    public static function create() {
        return new static();
    }

    static function getGroup() {
        return "default";
    }
}

class User extends DomainObject {
}

class Document extends DomainObject {
    static function getGroup() {
        return "document";
    }
}

class SpreadSheet extends Document {
}

print_r(User::create());
print_r(SpreadSheet::create());

Здесь в класс DomainObject я ввел конструктор, в котором используется ключевое слово static для вызова метода getGroup(). Стандартное значение группы сосредоточено в классе DomainObject, но оно переопределяется в классе Document.

Я также создал новый класс SpreadSheet, расширяющий класс Document. Вот что получим в результате.


User Object
(
[group: DomainObject:private] => default
)
SpreadSheet Object
(
[group: DomainObject:private] => document
)

Все происходящее с классом User не настолько очевидно, поэтому требует пояснений.

В конструкторе класса DomainObject вызывается метод getGroup(), который интерпретатор находит в текущем классе. Несмотря на это, в случае с классом SpreadSheet поиск метода getGroup() начинается не с класса DomainObject, а с класса SpreadSheet, для которого из метода create() был вызван стандартный конструктор.

Поскольку в классе SpreadSheet реализиция метода getGroup() не предусмотрена, интерпретатор вызывает аналогичный метод класса Document (т.е. идет вверх по иерархии объектов).

Таким образом, до появления PHP 5.3 и позднего статического связывания, здесь у нас возникала проблема из-за использования ключевого слова self, которое находило метод getGroup() только в классе DomainObject. Теперь же этот вопрос можно считать решенным.

На этом я завершаю данную статью. В следующем материале мы поговорим об обработке ошибок в PHP.

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Наверх