developer.co.ua

Holy Copypasters
19.10.2006
Алексей Гоголев

Singleton Pattern в PHP 0.71

В каких случаях использовать Singleton pattern


Singleton Pattern — один из наиболее известных шаблонов проектирования. Почти в любой объктно-ориентированной программе обычно существует один-два объекта, которые инициализируются в начале, и используются на всем протяжении работы приложения. Таким объектом может быть, logger — объект для ведения log-ов (log в переводе с англ. — журнал). Так в случае с log-ами, нам нет нужды каждый раз создавать объект класса logger, лишь для того, чтобы произвести запись в журнал. Достаточно создать один экземпляр класса, в начале работы программы, и пользоваться им. Такие классы (как например logger) будем называть singleton классами.

Важно отметить, что singleton pattern также решает еще одну наболевшую задачу. Часто бывает так, что мы передаем в функцию (метод) параметр лишь для того, чтоб эта функция передала этот параметр еще дальше. Или же несколько частей программы постоянно нуждаються в «свежей» информации о состоянии объекта, который меняется по ходу работы; тогда снова и снова приходиться передавать объект как параметр. Конечно, если такая ситуация возникает пару раз за всю работу приложения, можно не обращать на это внимания. Но, если один и тот же объект бестолково «футболяется» между всеми модулями и функциями программы, может возникнуть страшная путаница и, как следствие, ошибки. В таких ситуациях следует использовать singleton класс. Вместо того, чтоб передавать по цепочке объект из функции в функцию, singleton pattern обеспечивает возможность, каждой части програмы, получить по ссылке единственный для всей программы объект.

singleton
Расшифруем название: single — единственный (англ.) ton — мода, стиль (англ.). Правда название перекликается со смыслом?

Вот какие преимущества дает singleton pattern:

  1. Мы используем ссылки на один и тот же экземпляр класса в разных частях проекта. Таким образом не нужно создавать новый объект каждый раз когда мы хотим воспользоваться каким-то методом — просто пользуемся ссылкой.
  2. Теперь не нужно передавать объект как аргумент, чтоб все части программы были «в курсе» о текущем состоянии объекта. Все ссылки указывают на один объект.

Еще раз сформулируем суть 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?

  1. Не нужно опасаться, что наш единственный объект «подменится» по ходу работы приложения. Он надежно защищен от случайностей.
  2. Из любой части программы можно легко получить объект и пользоваться им. (и вовсе не нужно рассылать его, как параметр, всем функциям или создавать каждый раз заново, чтоб использовать какой-то метод).

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 как Антипаттерн, многопоточность

1 2 3 4 5

Последние комментарии:

Vov4ik
Значит ли то, что объект класса типа Singleton будет доступен в единственном экземпляре даже при вызове через $object = SingletonClass::getInstance() из любого *.php, присутстствующего в проекте? Если да, то зачем использовать global? Если нет, то зачем нужен Singleton???

Виктор
А вот не надо автоматизированный перевод за свой текст выдавать.

Re: Singleton + TDD Семен
Смотря как реализовать «одиночку». Если в класс добавить метод setInstance($instance), то отлично можно тестировать

Singleton+TDD Oleg Marchuk
Используйте одиночек аккуратно: юнит-тестирование кода с этим паттерном не возможно. Можно пробовать заменить одиночек на делегирование.

Обсудить (комментариев: 4)