задачка: abstract singletone

Voxus

founder (Старожил PHPCluba)
задачка: abstract singletone

...в процессе рефакторинга некоторое количество "статических помоек" идеологически мутировало в синглтоны. Чтобы не дописывать к каждому классу синглтонный код, т.е.:

PHP:
class Foo
{
	private static $instance;

	private function __construct();
	public static function getInstance() { /* ... */ }

	/* ... */
}
- захотелось заиметь один абстрактный синглтон и от него успешно занаследовать все эти объекты, т.е.:

PHP:
abstract class Singletone {/* ... */}

class Foo extends Singletone {/* ... */}
Итого, задачка: как можно наиболее красиво и оптимально реализовать этот абстрактный синглтон?

PS: задача имеет минимум одно не идеальное решение (ибо было бы оно идеальным - топик не родился бы).
 

Voxus

founder (Старожил PHPCluba)
насчёт абстрактного синглетона - фича недоступная на самом деле нигде, так что расстраиваться не стоит.
вполне доступная. c++/java/oberon/*insertYourLanguageHere*
 

vitus

мимо проходил
ну так расскажи, может я и неправ, буду рад что ошибался.
 

Voxus

founder (Старожил PHPCluba)
хочу подождать некоторое время - вдруг кто-нибудь решит по-другому?

например, AnToXa на IRC::#russian_programmer выдал решение в течении 5 минут с начала потока сознания ;-)
 

AnToXa

prodigy-одаренный ребенок
Автор оригинала: Voxus
хочу подождать некоторое время - вдруг кто-нибудь решит по-другому?

например, AnToXa на IRC::#russian_programmer выдал решение в течении 5 минут с начала потока сознания ;-)
два решения, прошу заметить :D
а ты как всегда ищешь идеал, похвально, но, похоже, не очень достижимо в данном случае.
 

vitus

мимо проходил
не томите, поделитесь :)
мне только супер извращения на ум приходят ... :(
ну или не совсем синглетон ...
 

camka

не самка
что-то я не понимаю, если у тебя два класса наследованы от одного и того же синглтона, то при инициализации правильный инстанс получит только первый.
PHP:
class db extends singleton{/*...*/}
class config extends singleton{/*...*/}

$db = db::get_instance();
$config = config::get_instance();
В этом случае $config получит объект класса db.

можно, конечно, сделать статический массив инстансов и обращаться по имени $db = db::get_instance('db');
 

vitus

мимо проходил
ну разве что так, хотя не уверен что станет работать, не проверял 5 сейчас недоступен

пинайте, не больно :)

PHP:
abstract class SingletonManager{
      private static $singletons=array();
      
      function __construct(){
           if(!isset($singletons[get_class($this)])) {
               $this=$singletons[get_class($this)];
               return;
           }
           $singletons[get_class($this)]=$this;
      }
      
}

class Foo extends SingletonManager{
    //no override constructor...
}

class Bar extends SingletonManager{
    //no override constructor...
}

$foo=new Foo();
$bar=new Bar();
 

vitus

мимо проходил
надож, а 4-й скушал, кстати я там зря воткнул "!" около isset() :)
 

su1d

Старожил PHPClubа
для РНР5 как одно из неидеальных решений:
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);
	}
}

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

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

print_r(Singleton::instance('C', 1,2,3));
print_r(Singleton::instance('C', 1,2,3));

?>
 

Profic

just Profic (PHP5 BetaTeam)
у меня это решено таким образом (возможно, что это помесь носорога с бегемотом, но меня устраивает :)

1) есть обычный класс, например тот же db (лежит в библиотеках)
2) есть класс лежащий в локальных инклюдах. этот класс реализует интерфейс singltonable и имеет суффикс _singleton (db_singleton), этот интерфейс описывает один метод getObject, который должен произвести инициализацию объекта и вернуть его.
3) класс singleton при запросе singleton::get ('db') загружает объект db_singleton и вызывает его метод getObject и складывает все это в массив по имени класса.
 

svetasmirnova

маленький монстрик
Voxus и АнТоХа
Я из-за вас заснуть не смогла :)
(ночью пост впервые увидела)
А если так:
PHP:
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 {
            new $class($class);
        } catch (singletonException $e) {
            //do nothing
        }
        return Factory::$instances[$class];
    }
}

class A extends Singleton {}
class B extends Singleton {}
$factory = new Factory();
$a1 = $factory->A();
$a2 = $factory->A();
$b1 = $factory->B();
$b2 = $factory->B();
var_dump($a1);
var_dump($a2);
var_dump($b1);
var_dump($b2);
[hr]
Think different &copy; Apple Computers
 

camka

не самка
В таком случае пропадает возможность вызывать объекты посредствам статического метода, и придется таскать повсюду этот объкт фабрики. Может быть это, конечно, вне условий задачи, но именно по этой причине я бы сей вариант использовать отказался.
 

su1d

Старожил PHPClubа
camka,
ну почему б не завести тогда статический метод, который лишь возвращал бы объект $factory?
PHP:
Factory::get()->A();
svetasmirnova
оч хороший способ, вот только если я захочу передавать свой особенный набор параметров конструктору...
 

vitus

мимо проходил
camka
Factory::getInstance()->A();

:))

но вообщето это всё вариации на тему не о синглетонах а о другом слове

-~{}~ 23.12.04 13:17:

упс, опоздал

-~{}~ 23.12.04 13:23:

интересно всётаки было бы узнать мнение инициаторов треда
 

camka

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

Profic

just Profic (PHP5 BetaTeam)
Кстати в догонку к моему посту, и как раз про синглтоны :)
Т.к. синглтон можно попросить создать экземпляр класса в любом месте, то возникает вопрос как быть с инициализацией этого объекта? Не писать же в каждом вызове singleton::getInstance еще и все нужные параметры конструктора? А если объект инициализируется через вызовы методов и установку свойств - их тоже писать после каждого вызова синглтона?

То, что я придумал сам по этому поводу написано в предыдущем моем посте. Что думает общественность по этому поводу?
 

Voxus

founder (Старожил PHPCluba)
svetasmirnova
congratulations :)

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

camka
Factory может выступать в данном случае как standalone Singleton (не занаследованный от абстрактного).

--

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

а при появлении аспектов (а вдруг?) - не нужно.

PS: на правах шутки: было еще решение с "раскручиванием" debug_backtrace() из статического getInstance() у абстрактного синглтона до последнего дитятки Singletone. ;-)
 
Сверху