Pull to refresh

Comments 61

Очень хорошая реализация, т.к. постоянное дублирование кода при наследовании, создании новых singleton'ов уже порядком надоело. Вместе с заплаткой для версий ниже 5.3.0 механизм получается универсальным.
наследование создано не для того, что бы избавляться от дублирования кода в одиночках.
мне кажется вы спутали принципы наследования. Наследовать нужно только тогда когда ребенок является более специфической реализацией родителя. Например дверь — железная дверь. А у вас получается наследуем потому что все синглтоны, а что они реализуют вообще не важно.
Согласен. Я сказал в постскриптуне, что наследование тут не самый удачный инструмент, но другого пути реализации в PHP я не вижу. У вас есть идеи?
я бы все таки использовал доступ к классам которые должны быть «синглтонами» через реестр. Zend_Registry как раз, например, для этого. Ибо в вашем случаем мне ничего не мешает сделать class MyClass extends Example и переопределить конструктор чтобы он мог создавать обьекты и тд и тп. Нет предела человеческой глупости :)
Singleton + Registry = Factory
Но вот по поводу глупости — я с Вами согласен.
Отвечу за автора =). Тут всё нормально: singleton — singleton для конфига.
Лучше фабрику (абстрактную, если нужно).
В шестой версии будет static и позднее связывание для статических методов. Вообще, думал, что это появилось уже в 5.3.
Ну как бы и написано, что появилось. И появилось собственно: )
так нельзя будет делать? static::$instance = new static();
Когда нельзя будет?
Когда доступно static::
В php 5.3+
Может быть там только методы так вызывать можно, но не конструктор?
Конструктор не проверял, но т.к. все делают через get_called_class видимо нельзя. Сам не проверял
Если так надо, используйте IoC контейнеры, Registry и т.п.

Кроме того, если честно, не очень понятно в какой ситуации данное решение может пригодится. Для написания самих синглтонов делается один макрос в IDE…

Про использование OOP уже написали выше. Добавлю что private конструктор, который потом легко так переопределяется в классах-потомках, сильно удивляет. Точнее, путает.

Да, и автоподстановка в IDE работать не будет в вашем случае. Ибо у метода getInstance() в документации не указать возвращаемый тип.

ЗЫ: Ваши строчки про аннотации из какого языка взяты? Java? В этом случае эти строчки вызывают крайне много вопросов.
сейчас на меня набросятся люди с топорами, НО:

class A
{
static $data;
static Bla()
{
echo self::$data;
}

static Init($str)
{
static $inited = false;
if(!$inited)
self::$data = $str;
}
}

A::Init('test');

в принципе такой код полностью симулирует синглтон, при этом его всё ещё можно наследовать и конструировать, тоесть разницы никакой, ну разве что

$a = new A;
$a->method();

или

A::method();

а собсна что вам нравится решайте сами
Сдается мне Вы не понимаете паттерна синглтон — вся суть которого в том, что объект контролирует число своих инстансов самостоятельно.
боюсь что «сути» этого чудо действия я не понимаю
особенно в свете приведённого в статье кода, который работает точно так же…
это потому что код статьи кривой
ну, возможно и кривой и реализует частный случай
однако замечу что паттерн это возможность а не руководство к действию, и в многих применениях такой код бывает полезен
ИМХО, синглтоны не стоят того, чтобы из-за них так мучиться.
Ибо, как всем известно, в php5 нет пронстранств имен и синглтоны ни чем не лучше использования глобальных переменных. Введете 2 синглтона с одинаковыми именами и придется делать рефакторинг всего проекта (((
Так что смысл синглтонами делать только классы шаблона Register, а в них уже хранить ссылки на остальные синглтоны.
Подробнее в книге www.books.ru/shop/books/693675
Поддерживаю. Если развить идею Registry со ссылками на объекты прямиком приходишь к паттерну Dependency Injection. А все эти singleton-ы это только минимальное решение проблемы времени жизни объектов.
Это PHP 5.3
У моего хостера, к примеру, установлен 5.2.11 :'-((
Что же мне делать?
если мне не изменяет память, то в php 5.3.* пространства имет таки имеются.
чтобы Singelton ваш был единственным используйте IoC(DI)
Буду очень, очень бладгодарен за пример инверсии контроля приминительно к данному случаю на PHP.
Очень хорошо всё описано в документации компонента Dependency Injection. Очень рекомендую.
Если бы речь шла о Java + Spring, то проблема, на сколько мне известно, действительно решалась бы объявлением следующего baen'а:

<bean id="example" class="package.ExampleImpl" singleton="true">

</bean>

Но в данном случае речь о не о Java.
Вместо is_a() лучше использовать instanceof, ЕМНИП :)
с версии 5.3 опять разрешили :)
Не стоит делать конструктор приватным, т.к. его нельзя будет переопределить в дочерних классах. Исправьте на протектед.
Когда количество синглтонов в проекте расте пропорционально сложности и размеру, стоит пересмотреть подход к архитектуре, т.к. подобный способ ломает модульность кода и сильно мешает юнит тестированию.

По-хорошему, класс должен сам знать все свои депенденси, так что особой нужды в жутких количествах синглтонов нет.
ну кстати ни разу не правы

модульность кода может достигаться за счёт использования указателей на объект
а вот класс не всегда знает свои зависимости, допустим если один и тот же класс работает с разными базами данных
тогда только 2 решения — или прокси на нужную базу или указатель на синглет работы с БД
>> модульность кода может достигаться за счёт использования указателей на объект
не понял мысль

>> а вот класс не всегда знает свои зависимости, допустим если один и тот же класс работает с разными базами данных
>> тогда только 2 решения — или прокси на нужную базу или указатель на синглет работы с БД
решение в духе ООП для такой ситуации — это интерфейс
синглтон для этого не нужен. Интерфейсов в пхп для БД много — PEAR::DB, adodb…
про казатель не очень точно выразился, идея следующая:

class DatabaseRecord
{
private $db;
function __construct($database)
{
self::$db = $database;
}

fucntion read()
{
$db = self::$db;
$db::doSomething();
}
}
абсолютно согласен — никакой нужды в синглтонах в данном примере нет =)
Да, но для получения имени вызываемого класса использовать его не получится.
Для создания синглтона это и не требуется
class Sin
{
public $a = 1;

static private $_instance;

static function getInstance()
{
if (! self::$_instance ) {
self::$_instance = new static; // тут только начиная с php 5.3.*
}

return self::$_instance;
}
}

class B extends Sin
{
public $a = 2;
}

$s = B::getInstance();

var_dump($s);
О! А этот код «компилируется»? :)
А то мы тут выше теоретические рассуждения разводим, работает это или нет — проверить негде.
Проверил — компилируется. И даже правильно работает ) Я почему-то ошибочно полагал, что через static можно обращаться только к методам/свойствам.
А почему вы пишете:
if (! self::$_instance ) {
self::$_instance = new static; // тут только начиная с php 5.3.*

а не
if (! static::$_instance ) {
static::$_instance = new static();
?
Или static:: работает только для методов, но не для полей? А скобочки после new static не нужны?
Оба способа монописуальны, так как при создании класса с помощью конструкции new в случае отсутствия параметров скобки можно опускать:

ini_set('display_errors', 1);
error_reporting(E_ALL | E_STRICT);

class Test {
    function __construct() {
        echo 'Test Constructor', "\n";
    }
}

$oTest1 = new Test(); // Печатает 'Test Constructor'
$oTest2 = new Test; // Печатает 'Test Constructor'
Потому что $_instance приватное свойство класса Sin, и не доступно для потомков. Вообще можно объявить $_instance как protected, тогда можно будет использовать static::$_instance. Я просто хотел чтобы было как у автора (private свойство), он вроде тоже об этом упоминает.

> А скобочки после new static не нужны?
Как вам больше нравиться =) Классы можно создавать и так и так. Т.е:

class B {}
$a = new B;

тоже вполне допустимо.

> Или static:: работает только для методов, но не для полей?
Нет, работает и так и так, как я уже писал здесь self только из-за того что поле private.
Да, не заметил приватности инстанции.
А в случае self::$_instance не перепишется приватное поле родительского класса?

Хотя все эти вопросы чисто теоретические — мне вряд ли светит переход на php6 :)
> Хотя все эти вопросы чисто теоретические — мне вряд ли светит переход на php6 :)
Ну в общем то php6 тут и не нужен, хватит php 5.3.

> А в случае self::$_instance не перепишется приватное поле родительского класса?
Гм, я не уверен, что понял, что именно вы спрашиваете. Если объявить в классе B static private $_instance, то нет, ничего не перепишется. Обращаясь к B self::$_instance и к Sin self::$_instance вы получите. Т.е когда модификатор свойства private потомок не может напрямую получить доступ к нему.

class Sin
{
    static private $_instance = 'Sin $_instance';
 
    public function test()
    {
        return self::$_instance;
    }
}
 
class B extends Sin
{
 
    static protected $_instance = 'B $_instance';
 
    public function test2()
    {
        return self::$_instance;
    }
}
 
$s = new B;
 
var_dump($s->test()); // Sin $_instance
echo '<Br>';
var_dump($s->test2()); // B $_instance


В промежуточном переходе на php5.3 смысла я не вижу, мне интересней нативная поддержка юникода в php6 — часто сталкиваюсь с проблемами с восточноевропейскими кодировками.

Вопрос был в другом:
class Sin
{
public $a = 1;

static private $_instance;

static function getInstance()
{
if (! self::$_instance ) {
self::$_instance = new static; // тут только начиная с php 5.3.*
}

return self::$_instance;
}
}

class B extends Sin
{
public $a = 2;
}

$b = B::getInstance();
$sin = Sin::getInstance();

Мне кажется, что переменные $b и $sin будут имет одно и то же значение — инстанцию класса B, так как он создастся раньше, но сохранится в общей для них поле self::$_instance в классе Sin.
Вот, теперь все ясно. Отвечаю (раз уж начал, то до конца) =) Да вы правы, и действительно свойство $_instance будет одно на всех. Для того чтобы это избежать, нужно делать что-то вроде, того что привел автор. Ну или объявлять static protected $_instance в потомках (и в родительском классе), и так же использовать везде static:: вместо self::. Я думаю можно еще найти какие-то способы, но все они мне почему-то не очень нравятся, ибо помахивают каким-то не здравым шаманством.
а мне нравится такой вариант:
$example_obj= $core->new_example;

где ядро самостоятельно находит файл 'inc/example.php', исполняет его получая имя класса, инстанцирует и кэширует результат.
Ох, уж эти префиксы в названиях переменных.
Чем дальше, тем все больше убеждаюсь, что singleton не для web. Он больше мешает, чем приносит пользы.
Sign up to leave a comment.

Articles