Алексей Гоголев
Singleton Pattern в PHP
В каких случаях использовать Singleton pattern
Singleton Pattern — один из наиболее известных шаблонов проектирования. Почти в любой объктно-ориентированной программе обычно существует один-два объекта, которые инициализируются в начале, и используются на всем протяжении работы приложения. Таким объектом может быть, logger — объект для ведения log-ов (log в переводе с англ. — журнал). Так в случае с log-ами, нам нет нужды каждый раз создавать объект класса logger, лишь для того, чтобы произвести запись в журнал. Достаточно создать один экземпляр класса, в начале работы программы, и пользоваться им. Такие классы (как например logger) будем называть singleton классами.
Важно отметить, что singleton pattern также решает еще одну наболевшую задачу. Часто бывает так, что мы передаем в функцию (метод) параметр лишь для того, чтоб эта функция передала этот параметр еще дальше. Или же несколько частей программы постоянно нуждаються в «свежей» информации о состоянии объекта, который меняется по ходу работы; тогда снова и снова приходиться передавать объект как параметр. Конечно, если такая ситуация возникает пару раз за всю работу приложения, можно не обращать на это внимания. Но, если один и тот же объект бестолково «футболяется» между всеми модулями и функциями программы, может возникнуть страшная путаница и, как следствие, ошибки. В таких ситуациях следует использовать singleton класс. Вместо того, чтоб передавать по цепочке объект из функции в функцию, singleton pattern обеспечивает возможность, каждой части програмы, получить по ссылке единственный для всей программы объект.
singleton
Расшифруем название: single — единственный (англ.) ton — мода, стиль (англ.). Правда название перекликается со смыслом?
Вот какие преимущества дает singleton pattern:
- Мы используем ссылки на один и тот же экземпляр класса в разных частях проекта. Таким образом не нужно создавать новый объект каждый раз когда мы хотим воспользоваться каким-то методом — просто пользуемся ссылкой.
- Теперь не нужно передавать объект как аргумент, чтоб все части программы были «в курсе» о текущем состоянии объекта. Все ссылки указывают на один объект.
Еще раз сформулируем суть singleton pattern: Убедиться, что существует только один экземпляр класса и обеспечить доступ к нему из любой части программы.
Недостаток «глобального» похода
Итак, мы хотим получить только одного представителя класса и использовать его везде, где нам заблагорассудится. Казалось бы, логично ввести глобальную переменную и не загружать себе голову всякими pattern-ами. Но тут есть подвох.
Представим, что мы решили использовать глобальную переменную, содержащую singleton объект. Все нужные нам свойства есть — переменная одна и доступна отовсюду. В чем же тогда проблема? Проблема в том, что глобальная переменная непредсказуемая: у нас нет гарантии, что, на протяжении работы приложения, в ней содержится тот же объект, который мы инициализировали в начале.
У этой проблемы есть решение, но оно кривое и страшненькое. На мой взгляд классический подход лучше (на то он и классический).
Как создать singleton класс
Очень просто. В классе необходимо создать статический метод getInstance(), который возвращает единственный экземпляр класса. Если этот метод вызывается первый раз, то экземпляр класса создается, сохраняется в приватном (private), статическом (static) поле, и возвращает свежесозданный объект. При повторном использовании метода, возвращается содержимое поля (т.е. экземпляр класса).
В следующем примере реализовано все вышесказанное.
<?php
// PHP5
class Logger {
static private $instance = NULL;
static function getInstance()
{
if (self::$instance == NULL)
{
self::$instance = new Logger();
}
return self::$instance;
}
private function __construct()
{
}
private function __clone()
{
}
}
//пусть далее размещены методы необходимые разработчику.
?>
Метод getInstance() — суть Singleton pattern. С помощью getInstance() мы можем получить доступ к объекту класса logger откуда угодно. В тоже время, методы constructor() и clone(), объявленные как приватные (private), обеспечивают единственность экземпляра класса logger. Удален источник возможных ошибок — разработчик не может случайно создать еще один объект такого же типа (Такой же принцип действует и при построении value object — убрать, спрятать все методы, с помощью которых, объекты смогут <размножаться>). Следовательно, getInstance()— единственный способ <достучаться> к представителю класса.
Что же вышло в итоге
Итак, нам удалось создать singleton класс. Мы обеспечили единственность экземпляра класса и организовали удобный доступ к нему. Что же мы выиграли, используя singleton pattern?
- Не нужно опасаться, что наш единственный объект «подменится» по ходу работы приложения. Он надежно защищен от случайностей.
- Из любой части программы можно легко получить объект и пользоваться им. (и вовсе не нужно рассылать его, как параметр, всем функциям или создавать каждый раз заново, чтоб использовать какой-то метод).
P.s.
Паттерн Singleton следует применять очень хорошо подумав, т.к. в некоторых случаях его можно рассматривать как Антипаттерн. Речь идет о том, что Singleton часто используют просто как глобальную переменную, и в этом случае к недостаткам глобальной переменной добавляется еще и проблема существования только одного экземпляра класса.Поэтому следует четко понимать для чего вы создаете Singleton — для глобального доступа (для чего, вообще говоря, он не предназначался), для реализации единственного экземпляра класса, либо для того и другого вместе. Если у вас слишком много Singleton'ов в приложении, возможно, есть смысл пересмотреть его дизайн.
Вероятно также, что в случае с PHP данная проблема вообще не актуальна, т.к. задачи типа создания многопотоковых приложений, или сетевых (когда один экземпляр класса используется несколькими приложениями одновременно) перед PHP изначально не ставились.
ссылки по теме:
1. php architect's Guide to PHP Design Patterns by Jason E. Sweat (php)
2. PHP 5 Advanced OOP and Design Patterns (php)
3. The Singleton Design Pattern for PHP (php) изложено коротко и ясно
4. Five common PHP design patterns (php) factory, singleton, observer, strategy и chain-of-command pattern
5. Wikipedia Singleton pattern по-английски
6. Vladimir Frolov: singleton is antipattern дискуссия по теме
7. Double-checked locking and the Singleton pattern (Java) Singleton как Антипаттерн, многопоточность
Последние комментарии:
Vov4ik | |
Значит ли то, что объект класса типа Singleton будет доступен в единственном экземпляре даже при вызове через $object = SingletonClass::getInstance() из любого *.php, присутстствующего в проекте? Если да, то зачем использовать global? Если нет, то зачем нужен Singleton??? | |
Виктор | |
А вот не надо автоматизированный перевод за свой текст выдавать. | |
Re: Singleton + TDD | Семен |
Смотря как реализовать «одиночку». Если в класс добавить метод setInstance($instance), то отлично можно тестировать | |
Singleton+TDD | Oleg Marchuk |
Используйте одиночек аккуратно: юнит-тестирование кода с этим паттерном не возможно. Можно пробовать заменить одиночек на делегирование. | |
Обсудить (комментариев: 4)