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)

  1. Спасибо за пример.
    Строка №46 var_dimp(), Вы имели в виду var_dump() ?

  2. Vhornets

    Спасибо, хорошо расписано.

  3. Хорошая статья, спасибо!

    А есть ли какое-то решение для передачи объектов в php-модуль средствами ajax? Или, перефразируя вопрос — как добиться передачи объектов внутри приложения при асинхронных запросах… используя паттерн registry-singleton…?

    Если в загружаемом асинхр… скрипте я вызываю

    $instance = Storage::getInstance ();

    то получаю новый пустой объект! Оно конечно и логично, т.к. ajax работает в рамках отдельного потока (поправьте если что), но вопроса это не снимает и как быть (чем заменить Singleton) не в полне понятно…возврат к глобальным переменным?

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

Добавить комментарий для Мурашов Олег Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *