Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Паттерн singleton

#ооп #php


Покажите на пальцах для чего нужен Singleton?
class Singleton {

    protected static $instance;  // object instance

    /**
     * Защищаем от создания через new Singleton
     *
     * @return Singleton
     */
    private function __construct() { /* ... */ }

    /**
     * Защищаем от создания через клонирование
     *
     * @return Singleton
     */
    private function __clone() { /* ... */ }

    /**
     * Защищаем от создания через unserialize
     *
     * @return Singleton
     */
    private function __wakeup() { /* ... */ }

    /**
     * Возвращает единственный экземпляр класса
     *
     * @return Singleton
     */
    public static function getInstance() {
        if ( is_null(self::$instance) ) {
            self::$instance = new Singleton;
        }
        return self::$instance;
    }

    public function doAction() { /* ... */ }

}

//usage
Singleton::getInstance()->doAction();

Для чего писать так? Если можно написать вот так:
class Singleton {
    public static function doAction() { /* ... */ }
}

Singleton::doAction();

Не проще, нет? %)
Только не нужно давать ссылок на википедию и т.п.

Всем спасибо! @knes, @ikoolik и @Nord001 — накинул вам по 25 очков за старание.    


Ответы

Ответ 1



Синглтон нужен, чтобы точно знать, что у нас создан ОДИН объект данного класса. Классический пример - коннект к базе данных. Если куча скриптов внутри одного файла теребит БД в разных функциях, приходится заводить глобальную переменную соединения и постоянно проверять жива ли она, на случай если в данный момент идет первое подключение к БД. Синглтон же сам создает соединение, если оно еще не было установлено, либо просто возвращает готовую ссылку. Вам же требуется только вызвать его, уже ни о чем не заботясь: лишний раз базу не потревожат. Второй ваш пример плох тем, что можно насоздавать кучу объектов данного класса, которые не будут знать что происходит у остальных.

Ответ 2



Синглтон - один из самых легких, но в то же время предельно значимых паттернов проектирования. Для чего он нужен, уже был дан ответ. Основная реализация показана вами, а вот плюшки каждый может привинтить свои. Так, например, в C++ можно создать отдельный шаблонный класс Singleton методом template, а далее уже делать из любого существующего класса синглтон.

Ответ 3



Выше писали про БД, я так ипользую: Singleton::getInstance('main')->doAction(); Singleton::getInstance('no_main')->doAction(); Поясню. Есть 3 сайта. 4 БД. Из них 3 БД - это БД с инфой конкретного сайта, а четвёртая БД это данные пользователей, т.к. на всех 3х сайтах они одинаковы. Поэтому когда мне надо сделать запрос к БД пользователей я использую: Singleton::getInstance('main')->doAction(); если допустим нужны статьи, то: Singleton::getInstance('no_main')->doAction(); Тем самым у меня гарантированно будет не более 1 подключения к конкретной БД. По сути можно делать как вы и написали: Singleton::doAction(); Но если вам нужно будет какие-нибудь данные, то вы всё равно будете вызывать >getInstance() для того что бы обратиться допустим к БД.

Ответ 4



Дело в том, что синглтон позволяет создать только один свой экземпляр. Соответственно все классы, в которых используется объект синглтона, используют один и тот же объект. С одними и теми же данными. Возьмем пример из ответа товарища @knes про соединение с БД. Если соединение с БД занесено в конструктор синглтона, то все классы использующие БД через синглтон используют одно и то же соединение. Еще пример: *** тут обьявление обычного класса `MyClass` с `private $a` и `getA(), setA()` *** $class1 = new MyClass(); $class2 = new MyClass(); $class2->setA(3); $class1->setA(5); echo $class2->getA(); //3 То же, если myClass - синглтон: $class1 = myClass::getInstance(); $class2 = myClass::getInstance(); $class2->setA(3); $class1->setA(5); echo $class2->getA(); //5 Все вышеизложенное - лишь наиболее понятная на мой взгляд иллюстрация механизма. Преимущества и недостатки синглтона - другая история.

Ответ 5



class Singleton { private static $PrS='init private'; public static $PuS='init public'; public static function PrS($A=false) { if($A!==false) self::$PrS=$A; else return self::$PrS; } public static function PuS($A=false) { if($A!==false) self::$PuS=$A; else return self::$PuS; } } echo Singleton::PrS(); echo "\n"; echo Singleton::PuS(); // выведет init private // init public echo "\n -- \n"; $D = new Singleton(); echo $D->PrS(); // также выведет init private echo "\n"; // init public echo $D->PuS(); echo "\n -- \n SET them all!"; // А вот здесь Singleton::PrS('changed private'); // меняем переменные класса Singleton::PuS('changed public'); // используя статическую ссылку echo "\n"; // и попробуем проверить их из "созданного" класса (хотя это просто ссылка копия) echo $D->PrS(); // разумеется, выведет: changed private echo "\n"; echo $D->PuS(); // changed public скопируйте этот простой пример, запустите его, и вам будет понятно, что статический класс, вместе с его статическими же переменными не дублируется оператором new. Да хоть десять дублей наделайте - все равно - статические переменные останутся. Слово static для функции говорит о том, что в глобальной таблице при компиляции ей уже выделен адрес - жестко выделен. Так же и со статическими переменными - их адрес также статичен. А НЕстатические переменные (классы) не существуют в адресном пространстве, пока их не определят (оператором new). Обращаться некуда. Для статических адрес уже есть - и к нему (к переменной) можно обратиться всегда - someStaticClass::value Хотите использовать статический класс для работы с БД - заведите внутри приватную статическую переменную DB_handler. Надо работать с несколькими соединениями (несколько БД) - заведите еще по необходимости. Можно даже соорудить нечто статического массива. Почему нет? Появится необходимость слишком извернуться - перепишите класс. Т.е. копии статических классов вообще не различаются (при изготовлении их оператором new), пока в них не появится хотя бы одна НЕстатическая переменная. Правда, и после этого различаться они будут только этой НЕстатической переменной. Правда, при этом, управлять этой переменной уже получится уже только из изготовленного класса. Далее: public static function getInstance() { if ( is_null(self::$instance) ) { self::$instance = new Singleton; } return self::$instance; } Вот этот кусок кода как раз и возвращает копию ссылки на адрес статического класса Singleton. Это то же самое, что написать Singleton::(и там что-то) Вот об этом и был вопрос - "ЗАЧЕМ?". Ответ простой - да, НЕЗАЧЕМ :) Наверное, есть задачи, где надо заводить экземпляр класса Singleton, "...а вот если не было обращений (ну не потребовалось что-то), то ничего не заведется и все будет тихо и спокойно... А вот вроде как статический класс будет существовать даже тогда, когда он может не понадобиться... и ух, как страшно съест памяти..." В общем, как-то я не могу вот так с ходу придумать такой задачи, чтобы применять именно СОЗДАНИЕ классов вместо статических классов. И вот я например, тоже не вижу разницы между сложным Singleton наворотом и простым Singleton::doAction(). Вообще, статические классы (со статическими переменными) чрезвыйчано удобны еще и тем, что они предоставляют как бы "глобальные" переменные для любой области видимости. И хэндлер для БД тому яркий пример.

Комментариев нет:

Отправить комментарий