Как стать автором
Обновить

Singleton и Late static binding

Время на прочтение 3 мин
Количество просмотров 8K
Количество Singleton'ов в проекте зачастую прямо пропорционально его сложности и размеру. Естественно, что описывать закрытый конструктор, статическое свойство-объект и метод его получения для сколь-либо ощутимого количества классов немного утомительно, да и пожалуй неверно. Отсюда встаёт вопрос: как «вынести за скобки» реализацию Singleton'а?

Вариантов тут на мой взгляд может быть несколько. Первый — пересмотреть архитектуру приложения. Возможно проще зарегистрировать все объекты, претендующие на Singleton, в некоторой коллекции, и обращаться к ним исключительно через неё. Это позволит избежать случайного создания двух копий объекта. Однако, с уверенностью утверждать, что в приложении в определённый момент выполнения существует только одна копия объекта, при таком раскладе нельзя, так как никто не может помешать создать объект напрямую, в обход коллекции.

Второй способ заключается в реализации суперкласса Singleton'а, от которого будут наследоваться все классы-Singleton'ы. Однако в его реализации на PHP есть один подводный камень — позднее связывание (late/dynamic binding) в статических методах. Точнее — определение имени текущего класса в статическом методе getInstance.
Стандартные средства вроде метода get_class тут применить нельзя, так как никакого объекта у нас ещё собственно нет. В голову приходит отражение, но быстро понимаешь, что тут оно ни причём, так как исходной точкой для него является имя объекта, которое мы собственно и пытаемся узнать.
И тут нам на помощь приходит механизм по названием «позднее статическое связывание» (late static binding), доступный начиная с версии 5.3.0. Суть его заключается в возможности сослаться на вызываемый класс в контексте статического наследования. Другими словами, начиная с версии 5.3.0 мы можем использовать функцию get_called_class для получения имени вызываемого класса в рамках статического метода.

Зная вышеописанное, нетрудно создать суперкласс для реализации шаблона Singleton.

  1. /**
  2. * Singleton pattern implementation
  3. */
  4. abstract class Singleton {
  5.  
  6.   /**
  7.    * Collection of instances
  8.    * @var array
  9.    */
  10.   private static $_aInstance = array();
  11.  
  12.   /**
  13.    * Private constructor
  14.    */
  15.   private function __construct(){}
  16.  
  17.   /**
  18.    * Get instance of class
  19.    */
  20.   public static function getInstance() {
  21.  
  22.     // Get name of current class
  23.     $sClassName = get_called_class();
  24.  
  25.     // Create new instance if necessary
  26.     if( !isset( self::$_aInstance[ $sClassName ] ) )
  27.       self::$_aInstance[ $sClassName ] = new $sClassName();
  28.     $oInstance = self::$_aInstance[ $sClassName ];
  29.     
  30.     return $oInstance;
  31.   }
  32.  
  33.   /**
  34.    * Private final clone method
  35.    */
  36.   final private function __clone(){}
  37. }
* This source code was highlighted with Source Code Highlighter.


Дополним код небольшим тестом

  1. class Example extends Singleton {}
  2. $oExample1 = Example::getInstance();
  3. $oExample2 = Example::getInstance();
  4. echo ( is_a( $oExample1, 'Example' ) && $oExample1 === $oExample2)
  5.   ? 'Same' : 'Different', "\n"
* This source code was highlighted with Source Code Highlighter.


результатом которого будет, как вы уже догадались, «Same».

Стоит заметить, что существуют механизмы получения имени вызываемого класса в контексте статической функции в PHP версии до 5.3.0 с помощью анализа стека вызова методом debug_backtrace. Однако, на мой взгляд, данный подход ректален чуть более, чем полностью.

P.S.
Согласен, что наследование — не лучший механизм в данном случае. На мой взгляд, если бы PHP поддерживал аннотации и АОП, было бы намного удобней помечать классы аннотацией Singleton и реализовать аспект, осуществляющий инъекцию необходимого для реализации Singleton'a кода в помеченные классы. Однако, на сколько мне известно, ни аннотаций (как конструкции языка), ни АОП в PHP в ближайшее будущее не планируется.
Теги:
Хабы:
+28
Комментарии 61
Комментарии Комментарии 61

Публикации

Истории

Работа

PHP программист
171 вакансия

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн