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.
Не стоит делать конструктор приватным, т.к. его нельзя будет переопределить в дочерних классах. Исправьте на протектед.
Когда количество синглтонов в проекте расте пропорционально сложности и размеру, стоит пересмотреть подход к архитектуре, т.к. подобный способ ломает модульность кода и сильно мешает юнит тестированию.

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

модульность кода может достигаться за счёт использования указателей на объект
а вот класс не всегда знает свои зависимости, допустим если один и тот же класс работает с разными базами данных
тогда только 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. Он больше мешает, чем приносит пользы.
Only those users with full accounts are able to leave comments. Log in, please.