Pattern Singleton – «Одиночка»
Паттерн Singleton (одиночка) многие ругают, зачастую, называя его анти паттерном. Тем не менее, он достаточно популярен и я пока не встречал крупных программных систем, где он не был бы реализован. Прежде всего, можно отметить фреймворки, где Singleton нередко выступает основой приложения. Также его часто наследуют компоненты, реализующие взаимодействие с конфигурационными данными или механизмом событий, например.
Основной проблемой данного паттерна является подкупающая простота его реализации и использования. Разработчики, не имеющие достаточного количества опыта, применяют Singleton там, где он совершенно не нужен, тем самым лишая программную систему гибкости и пригодности к адекватному тестированию.
Для того чтобы описать шаблон проектирования, сначала необходимо описать задачу, которую он будет решать.
Предположим, что в вашем приложении есть объект, в котором хранятся данные конфигурации приложения. Разумеется, данные конфигурации должны быть доступны разным частям приложения на всех уровнях.
Согласно идеологии ООП, для получения доступа к переменной, например, внутри метода объекта, ее необходимо передать в качестве параметра этого метода или конструктора. Необходимо стараться следовать этому принципу всегда, когда это возможно. Но, в описанной ситуации, трудно представить, через какое количество объектов придется протащить экземпляр класса работы с конфигом.
В PHP есть два способа поместить данные в глобальную область видимости, тем самым, сделать их доступными из любого места программы. Первый – использование суперглобального ассоциативного массива $GLOBALS, содержащего все переменные, объявленные в глобальной области видимости. Второй – использование ключевого слова global, вводящего переменную в текущую область видимости. Какой способ хуже, я затрудняюсь даже предположить. Как бы там ни было, в других языках программирование подобные средства отсутствуют и паттерн Singleton становится единственным способом введения объекта в глобальное пространство.
Класс, реализующий паттерн Singleton становится доступным глобально за счет статического интерфейса. Также к числу его особенностей необходимо отнести блокирование конструктора класса и магических методов __clone() и __wakeup(), описывая их с модификатором доступа private. Это делается для того, чтобы не допустить создание более одного объекта от класса.
<?php
class Singleton {
/**
* Singleton instance
*
* @var Singleton
*/
protected static $_instance;
/**
* Return singleton instance
*
* @return Singleton
*/
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self;
}
return self::$_instance;
}
public function Foo() {
$this->newPropetry = 'string';
}
public static function staticFoo() {
return self::getInstance();
}
private function __wakeup() {
}
private function __construct() {
}
private function __clone() {
}
}
Singleton::getInstance()->Foo();
var_dump(Singleton::staticFoo());
?>
Единственное свойство класса Singleton::$_instance хранит ссылку на экземпляр, который создается только при первом вызове статического метода Singleton::getInstance(). От повторного создания объекта уберегает условный оператор, с проверкой значения свойства и если ссылка на экземпляр уже существует, она будет возвращена.
Свойство Singleton::$_instance объявлено с модификатором protected, дабы класс можно было наследовать. Нередко паттерн реализуют с сокрытием этого свойства за модификатором private.
Реализацию шаблона Одиночка можно посмотреть, например, в классе CI_Controller из фреймворка CodeIgniter или Zend_Controller_Front из ZendFramework.
Комментарии (5)
Спасибо за пример.
Строка №46 var_dimp(), Вы имели в виду var_dump() ?
Да, конечно.
Спасибо, исправил опечатку.
Спасибо, хорошо расписано.
Хорошая статья, спасибо!
А есть ли какое-то решение для передачи объектов в php-модуль средствами ajax? Или, перефразируя вопрос — как добиться передачи объектов внутри приложения при асинхронных запросах… используя паттерн registry-singleton…?
Если в загружаемом асинхр… скрипте я вызываю
$instance = Storage::getInstance ();
то получаю новый пустой объект! Оно конечно и логично, т.к. ajax работает в рамках отдельного потока (поправьте если что), но вопроса это не снимает и как быть (чем заменить Singleton) не в полне понятно…возврат к глобальным переменным?
Тут паттерны вам не помогут и никакого отношения к вопросу не имеют, как и глобальные переменные. Вам нужно сохранять состояние приложения между его запусками. Если нужно сохранить именно объект, можно сериализовать его и далее поместить в какое-либо хранилище: база данных, память, плоские файлы и т.п.