задачка: abstract singletone

camka

не самка
простите за назойливость. При попытке универсализировать все предыдущие решения застрял не таком:
PHP:
<?php
error_reporting (E_ALL | E_STRICT);
class singletonException extends Exception { 
    public function __construct() { 
        //do nothing 
    } 
} 
abstract class Singleton { 
    public function __construct($name) { 
        if(isset(Factory::$instances[$name])) { 
            throw new singletonException(); 
        } else { 
            Factory::$instances[$name] = $this; 
        } 
    } 
} 
final class Factory { 
    static $instances = array(); 
    public function __call($class, $args) { 
        try {
		$R = new ReflectionClass($class); 
        	call_user_func_array(array($R, 'newInstance'), $args);
           	//new $class($class); 
        } catch (singletonException $e) { 
            //do nothing 
        } 
        return Factory::$instances[$class]; 
    }
    
    public static function get()
    {
    	return new self;
    }
} 

class A extends Singleton 
{
	public $a;
	public function __construct($a)
	{
		$this->a = $a;
		parent::__construct(__CLASS__);
	}
} 
class B extends Singleton 
{
	public $b;
	public $c;
	public function __construct($b, $c)
	{
		$this->b = $b;
		$this->c = $c;
		parent::__construct(__CLASS__);
	}
} 
echo '<pre>';
$a1 = Factory::get()->A(1); 
$a2 = Factory::get()->A(2); 
$b1 = Factory::get()->B(3,4); 
$b2 = Factory::get()->B(4,5); 
var_dump($a1, $a2, $b1, $b2); 
?>
вылетает сегментейшн фолт.
 

su1d

Старожил PHPClubа
было еще решение с "раскручиванием" debug_backtrace() из статического getInstance() у абстрактного синглтона до последнего дитятки Singletone. ;-)
на РНР 5.1-dev у меня это не сработало.
в поле "Class" от debug_backtrace() получаем, что вызов пошёл не от того класса, откуда вызывали, а от того, где нарисовали метод.

-~{}~ 23.12.04 12:08:

camka,
зачем так много писать?
зачем так много классов?
присмотрись к моему примеру.
ведь его можно чуть-чуть изменить:
PHP:
<?php

class Singleton {
	static function instance() {
		static $instances;
		if(!isset($instances)) $instances = array();

		$className = array_shift($args = func_get_args());
		if(isset($instances[$className])) 
			return $instances[$className];

		$R = new ReflectionClass($className);

		return $instances[$className] = call_user_func_array(
			array($R, 'newInstance'), 
			$args
		);
	}

	protected function __constructor() {
	}

	static function get() {
		return self::instance(__CLASS__);
    }

    function __call($method, $params) {
    	array_unshift($params, $method);
    	return call_user_func_array(
    		array(__CLASS__, 'instance'), 
    		$params
    	);
    }
}


class A {
	function __construct() {
		$params = func_get_args();
		echo __METHOD__."(".implode(', ', $params).")\n";
	}
}

class B extends A {}
class C extends B {}

var_dump(Singleton::get()->C(1,2,3));
var_dump(Singleton::get()->C(3,4,5));

?>
да, от Singleton'а никто не наследует (но это, по-моему, будет и правильнее: Молоко и Гвозди не должны иметь общих предков), но функционал -- тот же самый.
 

camka

не самка
а чем, собственно, такое решение будет отличаться от создания простой функции со статическим массивом инстансов, кроме как красотой написания? По сути дела использование имени метода вместо имени класса - это то же самое извращение, что и передача того же имени в строке.

$a = get_instance('A', 1, 2, 3);

где А - это имя класса, а все последующие элементы - это аргументы передаваемые в конструктор этого класса.
 

svetasmirnova

маленький монстрик
su1d
>оч хороший способ, вот только если я захочу передавать свой особенный набор параметров конструктору...
У __call() второй аргумент - массив параметров, так что не вижу с этим проблемы: мысль же нужно было передать, а не конкретную реализацию
camka
>тогда, каждый раз при инициализации того или иного класса, наследованного от синглтона, будет создаваться новый >объект фабрики. Или это не страшно?
Фабрику можно синглтоном сделать (впрочем, Voxus то же самое ответил)
Voxus
>было еще решение с "раскручиванием" debug_backtrace() из статического getInstance() у абстрактного синглтона до >последнего дитятки Singletone. ;-)
И у меня тоже, но лень было делать :)
>мое решение, если не считать минорных идеалогоических различий, - идентично предложенному.
По большому счёту - жаль: хотелось бы четвёртый вариант
vitus
>не Singletonы это всё,
>не бывает Abstract Singleton, по определению
Можно поинтересоваться: а что?
 

vitus

мимо проходил
svetasmirnova
что-же это такое? ...
ответить одним словом сложно, - это комбинированное решение по организации суперглобального массива для хранения объектов одного типа.
(возможно существует стандартный паттерн)

как назвать? да хоть вот так:

Singlet - конкретные классы (у тебя A и B)
GenericSinglet - то что вы назвали Abstract Singleton
SingletContainer - то что вы назвали Factory

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

Voxus Singleton - термин у которого есть определение.

ЗЫ: да, я - зануда :)
 

svetasmirnova

маленький монстрик
Что-то я решила к вам всем прислушаться и сделать из прототипа неидеальный, но рабочий вариант.
Получилось 2 варианта :)
vitus
Перебросила из фабрики в Singleton контейнер объектов.
Так как конструктор стал protected, то создавать экземпляры Singleton можно только из его деток.
Если есть ещё варианты: не таи :)
su1d
Заодно и pattern (если это можно так назвать) обработки аргументов сделала.
camka
Фабрика тоже Singleton.

Вот так теперь выглядит первый вариант:
PHP:
class singletonException extends Exception {
    private $_instance;
    public function __construct($instance) {
        $this->_instance = $instance;
    }
    public function getInstance() {
        return $this->_instance;
    }
}
abstract class Singleton {
    protected function __construct($class, $args = null) {
        static $_instances = array();
        if(isset($_instances[$class])) {
            throw new singletonException($_instances[$class]);
        } else {
            $_instances[$class] = $this;
        }
    }
}
final class Factory extends Singleton {
    public static function getInstance($class = null, $args = null) {
        if (null === $class) {
            $class = __CLASS__;
        }
        try {
            return new $class($class, $args);
        } catch (singletonException $e) {
            return $e->getInstance();
        }
    }
    public function __call($class, $args) {
        return Factory::getInstance($class, $args);
    }
}

class A extends Singleton {
    public function __construct($class, $args = null) {
        parent::__construct($class, $args);
        //do something with $args
    }
}
class B extends Singleton {
    public function __construct($class, $args = null) {
        //do something with $args
        parent::__construct($class, $args);
    }
}
$a1 = Factory::getInstance()->A();
$a2 = Factory::getInstance()->A();
$b1 = Factory::getInstance()->B();
$b2 = Factory::getInstance()->B();
var_dump($a1);
var_dump($a2);
var_dump($b1);
var_dump($b2);
А это второй. Без фабрики.
voxus
А Singleton обязательно abstract должен быть?
А то можно так сделать:
PHP:
class singletonException extends Exception {
   //look up 
}
class Singleton {
    public static function getInstance($class = null, $args = null) {
        if (null === $class) {
            $class = __CLASS__;
        }
        try {
            return new $class($class, $args);
        } catch (singletonException $e) {
            return $e->getInstance();
        }
    }
    public function __call($class, $args) {
        return Singleton::getInstance($class, $args);
    }
    protected function __construct($class, $args = null) {
        static $_instances = array();
        if(isset($_instances[$class])) {
            throw new singletonException($_instances[$class]);
        } else {
            $_instances[$class] = $this;
        }
    }
}
А если Singleton оставить абстрактным, то так:
PHP:
class singletonException extends Exception {
    //look up
}
abstract class Singleton {
    public static function getInstance($class = null, $args = null) {
        if (null === $class) {
            $class = 'Instance';
        }
        try {
            return new $class($class, $args);
        } catch (singletonException $e) {
            return $e->getInstance();
        }
    }
    public function __call($class, $args) {
        return Singleton::getInstance($class, $args);
    }
    protected function __construct($class, $args = null) {
        static $_instances = array();
        if(isset($_instances[$class])) {
            throw new singletonException($_instances[$class]);
        } else {
            $_instances[$class] = $this;
        }
    }
}
class Instance extends Singleton {}
То есть пустую Instance добавить.
Используем одинаково:
PHP:
class A extends Singleton {
    //look up
}
class B extends Singleton {
    //look up
}
$a1 = Singleton::getInstance()->A();
$a2 = Singleton::getInstance()->A();
$b1 = Singleton::getInstance()->B();
$b2 = Singleton::getInstance()->B();
var_dump($a1);
var_dump($a2);
var_dump($b1);
var_dump($b2);
 

vitus

мимо проходил
ну конечно есть :)
PHP:
class A extends Singleton { 
    //look up 
} 
class B extends Singleton { 
    //look up 
} 
$a = Singleton::getInstance()->A(); 
$b = Singleton::getInstance()->B(); 

//вот такой например
$a->getInstance()->B();
$b->getInstance()->A();
//или вот такой 
A::getInstance()->A();
собственно я почему так упрямо твержу что это не синглетон?
потомучто :

PHP:
class C extends A{
}

$a=Singleton::getInstance()->A();
$c=Singleton::getInstance()->C();

echo ($a InstanceOf A); 
echo ($c InstanceOf A); 
//здесь он скажет что A - не синглетон
у класса A два инстанса - для синглетона это недопустимо
 

svetasmirnova

маленький монстрик
vitus
>//здесь он скажет что A - не синглетон
А и вправду :)
>$a->getInstance()->B();
Поэтому мне вариант с фабрикой в чём-то больше нравится (использовать так можно, но логично накладывает ограничения), несмотря на вроде бы лишний код.

Тем не менее, чем меня эта идея зацепила: ну за:)сь я синглетоновый код везде добавлять. Вот сделаю идеал и тогда...

Кстати, к предыдущему посту: шорты и майка - вполне корректный женский костюм-двойка.
 

Voxus

founder (Старожил PHPCluba)
Автор оригинала: svetasmirnova
Тем не менее, чем меня эта идея зацепила: ну за:)сь я синглетоновый код везде добавлять. Вот сделаю идеал и тогда...
на правах идеи: может, стОит поднять репозиторий для подобных решений? а позже, при желании, задокументировать и "втюхать массам" (с)

из актуального (на мой взгляд):
1) собственно, текущий сабж
2) sql query builder
3) user input validation (form get/post)
4) dao's

с последним пунктом - вообще интересностей невозможное количество. в идеале там можно добиться persistence layer'а, построенного на единовременной генерации dao-классов/иерархии из какой-нибудь xml'ины и/или подобного.
 

vitus

мимо проходил
Тем не менее, чем меня эта идея зацепила: ну за:)сь я синглетоновый код везде добавлять. Вот сделаю идеал и тогда...
сделай темплету и инклюдь её где надо, - вот и весь секс :)

-~{}~ 27.12.04 16:23:

упз, погорячился нащёт "инклюдь темплету", сложновато реализовать, сам ещё не придумал как :)
 

svetasmirnova

маленький монстрик
Voxus
>может, стОит поднять репозиторий для подобных решений?
А почему нет? Давайте попробуем. Могу у себя на сервере организовать.
vitus
Несколько расширив пример с instance, можно доказать, что НЕ final синглтонов также не бывает:
PHP:
class Singleton {
	static $instance = null;
	private function __construct() {}
	public static function instance() {
		if (Singleton::$instance == null) {
			Singleton::$instance = new Singleton;
		}
		return Singleton::$instance;
	}
}
class A extends Singleton {
	private function __construct() {}
	public static function instance() {
        static $instance = null;
		if ($instance == null) {
			$instance = new A;
		}
		return $instance;
	}
}

$a = Singleton::instance();
$b = A::instance();
var_dump($a);
var_dump($b);
var_dump($a instanceof Singleton);
var_dump($b instanceof Singleton);
 

vitus

мимо проходил
можно доказать, что НЕ final синглтонов также не бывает
строго говоря - да

насчёт темплет, - ничего путного кроме автогенерации кода не придумал, но мне кажется что это не стоит таких затрат.

меня продолжает мучить вопрос: где вам удаётся брать столько синглетонов, чтобы задолбаться их реализовывать?
ну один-два, это ещё понятно ...

-~{}~ 28.12.04 10:55:

из актуального (на мой взгляд):
1) собственно, текущий сабж
2) sql query builder
3) user input validation (form get/post)
4) dao's

с последним пунктом - вообще интересностей невозможное количество. в идеале там можно добиться persistence layer'а, построенного на единовременной генерации dao-классов/иерархии из какой-нибудь xml'ины и/или подобного.
не расценивайте как наезд плиз:

1) - суперглобал, у нас уже есть $GLOBALS, всё остальное - "Самоходный Стационар" (c) :)
2) - в каком смысле? (это может быть действительно интересно)
3) - тип данных, допустимость значения, уникальность записи. или что-то иное имеется в виду?
4) - поясните плиз, чем конфиг лучше скрипта? имхо скрипт гибче.
persistence layer на пхп ?
 

Voxus

founder (Старожил PHPCluba)
Автор оригинала: svetasmirnova
Voxus
>может, стОит поднять репозиторий для подобных решений?
А почему нет? Давайте попробуем. Могу у себя на сервере организовать.
ок, я тогда в приват перейду.

--

Автор оригинала: vitus
1) - суперглобал, у нас уже есть $GLOBALS, всё остальное - "Самоходный Стационар" (c)
а это к чему? я про текущую задачку речь вел. не понимаю, при чем здесь superglobals.
2) - в каком смысле? (это может быть действительно интересно)
проще примером ответить:
PHP:
	$query = OSQL::select()->from('someTable')->
		get('id')->get('name')->
		where(Expression::eq('id', $yetAnotherId))->
		orWhere(Expression::like('name', 'coolName'));

	echo $query->toString();
получаем (при pgsql диалекте):
Код:
SELECT id, name FROM someTable WHERE ("id" = '123') OR (name ~* 'coolName')
3) - тип данных, допустимость значения, уникальность записи. или что-то иное имеется в виду?
имеется ввиду валидация данных, полученных от пользователя. во что это выродится - я не знаю. недавно в этом форуме был длинный топик по этому вопросу.
4) - поясните плиз, чем конфиг лучше скрипта? имхо скрипт гибче.
не понял ни вопроса, ни к чему он. поэтому отвечу стандартно: что лучше - слоны или апельсины?
persistence layer на пхп ?
согласен, звучит ужасно. но с другой стороны - окидывая взглядом текущую иерархию dao в одном из проектов - появляется подозрение, что все это могло бы быть сгенерировано из самого обыкновенного mapping'а.
а может быть оно и не нужно, а может быть это буддет слишком дорого стОить. пока что не знаю. :)
 

Макс

Старожил PHPClub
проще примером ответить:

$query = OSQL::select()->from('someTable')->
get('id')->get('name')->
where(Expression::eq('id', $yetAnotherId))->
orWhere(Expression::like('name', 'coolName'));

echo $query->toString();

получаем (при pgsql диалекте):

code:
SELECT id, name FROM someTable WHERE ("id" = '123') OR (name ~* 'coolName')
а в чем выигрыш ? Размер кода, который пишешь больше чем полученый SQL-запрос.
Или все это для переносимости на другую БД ?
 

Voxus

founder (Старожил PHPCluba)
Автор оригинала: Макс
а в чем выигрыш ? Размер кода, который пишешь больше чем полученый SQL-запрос.
Или все это для переносимости на другую БД ?
гибкость.

не хочу просто повторяться - подобные вещи уже очень подробно и неоднократно обсуждены, описаны и широко используются.
 
Сверху