Вопрос для гуру, это - Singleton

Mihail

Guest
Вопрос для гуру, это - Singleton

Я создал простой пример:

PHP:
<?php

 class B {

  var $q=Null;

   function __construct($o) {
    if ( is_null($this->q) ) {
      $this->q=$o;
     }
     return $this->q;
   }

 }

 class A extends B{
   function &__construct() {
      return  parent::__construct($this);
    }

   function &instance() {
    return $this;
   }
 }
?>
Я считаю, что класс A является одиночкой, это так?
:confused:
 

camka

не самка
Не совсем в тему, но:

- перемешан PHP и PHP5 синтаксис
- конструктор не возвращает значения
 

pachanga

Новичок
Это каша, а не singletone :(

Код:
class A
{
  static protected $instance = null;

  private __construct(){}
  
  static function instance()
  {
    if(!A :: $instance)
      A :: $instance = new A();

    return A :: $instance;
  }
 

Mihail

Guest
Сорри за первый пост, ввела в заблуждение ошибка в сравнении объектов в PHP 4.

Предлагаю рассмотреть следующий код:

PHP:
<?php

 class A {
  public static $q=NULL;

  final function  A() {
    if ( is_null(A::$q) ) {
      A::$q=$this;
     } else {
        throw new Exception("Object is created once !!!");
     }
   }

   function Y(){
   printf(' -- Y --');
   }
 }

 $a=Null;

 try {
   $a=new A();
 }
 catch (Exception $e){
   $a=NULL;
 }

print_r(var_dump($a));

$a instanceof A ? printf('Yes') : printf('No');

$a->Y();

 try {
   $a=new A();
 }
 catch (Exception $e){
   $a=NULL;
 }

print_r(var_dump($a));

$a instanceof A ? printf('Yes') : printf('No');

$a->Y();
?>

Класс А Singelton ?
 

pachanga

Новичок
Посмотри еще раз на вариант, который я тебе раньше предложил. Чем тебя он может не устраивать? Выдумываешь колесо?
 

Mihail

Guest
Автор оригинала: pachanga
Посмотри еще раз на вариант, который я тебе раньше предложил. Чем тебя он может не устраивать? Выдумываешь колесо?
Все ОК, но мне не все нравится в этом коде.

Почему?

Блокирование private конструктора дает нам единственный способ породить ссылку на объект через метод instance().
Так как объект еще не создан, единственным способом вызвать instance() остается сделать его static методом.
Упомяну, за счет private конструктора нельзя произвести наследование, и вызов конструкции $a = new A().

Мне не нравится только две вещи:

1. Расплодившиеся статические методы.
2. Нельзя прервать конструктор без выделения памяти под
переменную.

Поясню свою точку зрения.

1. В любом нолрмальном языке, нельзя вызвать метод еще не созданного объекта.

2. В нормальном классе не должно быть ограничений на стандартные конструкции языка, то есть $a = new A() должно выполняться в любом случае. Он может вызывать ошибки, но должен выполняться.


А Вот и мое колесо :D

( The Singleton example on PHP5)

PHP:
<?php

 class SingletonException extends Exception
 {
    private $pointer=NULL;

    public function __construct(&$message, $code = 0) {
        // some code
        $this->pointer=$message;

        $message='';
        // make sure everything is assigned properly
        parent::__construct($message, $code);
    }

    // custom string representation of object */
    public function __toString() {
     //   return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
    }

    public function& getpointer_tosingleton() {
       return $this->pointer;
    }
 }

 class A {
  private static $q=NULL;
  private static $exp;
  private $zasada='';

  final function A() {
    if ( is_null(A::$q) ) {
      A::$q=$this;
      A::$exp = new SingletonException(A::$q);
     } else {
       // ??? при повторном new SingletonException(A::$q); PHP начинал глючить, кто объяснит?
        throw A::$exp;
       }
    }

  function Y($message=NULL){
   if (!is_null($message)) $this->zasada=$message;
     printf($this->zasada."\n");
  }

 }


 $a=Null;

 try {
   $a=new A();
 }
 catch (Exception $e){
   echo "throw 1 \n";
   $a=$e->getpointer_tosingleton();
 }

print_r(var_dump($a));

$a instanceof A ? printf("Yes \n") : printf("No \n");

$a->Y(' This is A');

//--------------------------

try {
   $b=new A();
 }
 catch (Exception $e){
   echo "throw 2 \n";
  // print_r(var_dump($e));
   $b=$e->getpointer_tosingleton();
 }

print_r(var_dump($b));

$b instanceof A ? printf("Yes \n") : printf("No \n");

$b->Y(' This is B');
$a->Y();


//--------------------------
try {
   $c=new A();
 }
 catch (Exception $e){
   echo "throw 3 \n";
  // print_r(var_dump($e));
   $c=$e->getpointer_tosingleton();
 }

print_r(var_dump($c));

$c instanceof A ? printf("Yes \n") : printf("No \n");

$c->Y(' This is C');
$a->Y();
$b->Y();

//--------------------------
try {
   $d=new A();
 }
 catch (Exception $e){
   echo "throw 4 \n";
  // print_r(var_dump($e));
   $d=$e->getpointer_tosingleton();
 }

print_r(var_dump($c));

$d instanceof A ? printf("Yes \n") : printf("No \n");

$d->Y(' This is D');
$a->Y();
$b->Y();
$c->Y();

?>
 

pachanga

Новичок
Ну, ну....делать наследование для паттерна singleton, нет слов.

-~{}~ 19.08.05 13:05:

Блокирование private конструктора дает нам единственный способ породить ссылку на объект через метод instance().
Это и есть реализация паттерна. То что ты делаешь, ни в какие ворота не вписывается.
 

Mihail

Guest
А вызов метода еще не созданного объекта - в какие ворота вписывается?
 

pachanga

Новичок
Ты про static слышал?

-~{}~ 19.08.05 14:31:

Ликбез:
http://c2.com/cgi/wiki?PhpSingleton
http://c2.com/cgi/wiki?JavaSingleton
 

Mihail

Guest
Я все это видел.
Тебя не устраивает отсутствие метода instance()

Вот так он будет выгдядеть:

function &instance(){
return $this;
}

Вот так вызываться:
$f=$a->instance();

print_r(var_dump($f));
$f->Y(' This is F ');

Заметь, что пока не будет создан объект $a, ссылку нельзя будет получить.

Я не пойму о чем речь.
Я обеспечил уникальность объекта?
Возможно еще создать его копию?
 

Profic

just Profic (PHP5 BetaTeam)
Блокирование private конструктора дает нам единственный способ породить ссылку на объект через метод instance().
А чем это плохо?
Так как объект еще не создан, единственным способом вызвать instance() остается сделать его static методом.
И это?
Упомяну, за счет private конструктора нельзя произвести наследование, и вызов конструкции $a = new A().
1) Сделай его protected.
2) Делать наследников от синглтона - моветон.

1. Расплодившиеся статические методы.
Где они плодятся? На каждый класс один статик для синглтона.
2. Нельзя прервать конструктор без выделения памяти под переменную.
1) Покажите хоть один язык, где можно прервать конструктор, без выделения памяти.
2) Мне очень интересно: это вообще как? Конструктор работает с уже "выделенной" памятью для объекта. Да и в php об этом лучше не задумываться, тем более что ничего изменить нельзя.

1. В любом нолрмальном языке, нельзя вызвать метод еще не созданного объекта.
В любом нормальном языке можно вызвать метод класса без создания объекта этого класса.
2. В нормальном классе не должно быть ограничений на стандартные конструкции языка, то есть $a = new A() должно выполняться в любом случае. Он может вызывать ошибки, но должен выполняться.
1) $a = new A(); - нормальная конструкция языка, но вот try/catch вокруг каждого $a = new A() - нет. Вы вообще применяете исключения не по назначению.
2) Кто вам сказал, что класс, реализующий паттерн синглтона - "нормальный"
Возможно еще создать его копию?
1) $e = clone $f;
2) $e = unserialize(serialize($f));
 

Mihail

Guest
1) $e = clone $f;
2) $e = unserialize(serialize($f));

Вот тебе ответ:

function __clone(){
throw A::$exp;
}

function __sleep(){
throw A::$exp;
}


Вот так будут вызваны, в противном случае Error
try {
$g = clone $f;
}
catch (Exception $e) {
$g = $e->getpointer_tosingleton();
}
print_r(var_dump($g));
$f->Y(' This is g ');
$g->Y();

//--------------------------

try {
$h = unserialize(serialize($g));
}
catch (Exception $e) {
$h = $e->getpointer_tosingleton();
}

print_r(var_dump($h));

В каком таком языке, подчеркиваю - неинтерпретируемом можно вызвать метод класса без создания объекта?

Я сделал try/catch только для контроля ошибки, а мог бы просто выдавать сообщение: "Error ! Dont do it, it is Singleton!".
 

itprog

Cruftsman
Другой немного вопрос.
Есть синглетон:
PHP:
 ...
    if(!A :: $instance)
      A :: $instance = new A();
 ...
А что делать если конструктору требуется передать аргумент? (например класс A($file) работает с текстовым файлом '$file'). Передавать нужный аргмуент instance() не есть гуд, код захломляется.
 

Profic

just Profic (PHP5 BetaTeam)
Хе-хе, я кажется понял в чем основное заблужение автора топика.

В каком таком языке, подчеркиваю - неинтерпретируемом можно вызвать метод класса без создания объекта?
Вызов метода класса без создания объекта это есть статический вызов. Например, С++:
PHP:
template<typename ClassT>
class Singleton {
	private: static ClassT* s_instance;
	public: static bool set(ClassT* a_instance);
	public: static ClassT* reset();
	public: static ClassT* get();
};

template<typename ClassT>
ClassT* Singleton<ClassT>::s_instance = NULL;

template<typename ClassT>
bool
Singleton<ClassT>::set(ClassT* a_instance) {
	if (s_instance) return false;
	s_instance = a_instance;
	return true;
}

template<typename ClassT>
ClassT*
Singleton<ClassT>::reset() {
	ClassT* l_instance = s_instance;
	s_instance = NULL;
	return l_instance;
}

template<typename ClassT>
ClassT*
Singleton<ClassT>::get() {
	return s_instance;
}

class Foo : public Singleton<Foo> {
	public: void doSomething() { std::cout << "Hey, I'm relly doing some stuff\n"; }
}

int
main() {
	Foo::set(new Foo);
	Foo* l_foo = Foo::get();
	l_foo->doSomething();
	delete Foo::reset();
}
Этот код на работоспособность я не проверял, однако нечто подобное работает, причем успешно.

Я сделал try/catch только для контроля ошибки, а мог бы просто выдавать сообщение: "Error ! Dont do it, it is Singleton!".
Синглтон нужен не для того, что бы нельзя было создать несколько экземпляров класса, а для удобного доступа к единому экземпляру класса из различных мест не прибегая к глобальным переменным. Разницу чуешь? Эта пелена ненужного кода, всего лишь запрещает создать несколько экземпляров класса. И только как побочный эффект, через свойство брошенного исключения, можно получить доступ к экземпляру. Так по мне
PHP:
$obj = Class::instance()
намного более понятно, чем
PHP:
try {
	$obj = new Class();
} catch (SingletonException $e) {
	$obj = $e->getpointer_tosingleton();
}
, и главное меньше писать.
 

whoarym

Guest
ммм... уважаемый Mihail... никого не хочу оскорбить... но вы понимаете КАКОЙ код пишете? несмотря на то, что Вам объясняют, как делать правильно...

Мною лично написаны тонны кода на PHP и Java, так что боюсь, я понимаю тот код, который пишу... так вот... вам советуют ПРАВИЛЬНО. Не изобретайте велосипед, почитайте, что вам пишут более опытные коллеги.
 

Mihail

Guest
Я привел вариант решения и спросил так - это Singleton или не Singleton. Я приследовал определенные цели и я их достиг.

По поводу комбинации с Exception, хочу сказать:
Повторюсь, я точно также как в варианте с классом содержащим static instance() мог бы выдавать ошибку при попытке вызова конструктора, но я показал как можно вернуть указатель на объект в результате "ошибки". Это ситуация может возникуть по причине того, что вы элементарно могли забыть создавали вы или не создавали Singleton.
Обработка ошибки позволила продемонстрировать несколько примеров последовательно, что при выдачи ошибки было бы затруднительно.

По поводу С++ кода.

Я достаточно хорошо разбираюсь в С++.
Для меня, приведеный выше код, может проявить уважение к Вам, но как пример кода - это сложный неудобочитаемый кусок. Если бы так все книги объясняли теорию, я думаю никто С++ не выучил.
Хочу заметить - практически без ошибок.
У меня стоит gcc 3.2 все скомпилировалось в полтычка.

Я СОГЛАСЕН, Я БЫЛ НЕ ПРАВ – С++ ДОПУСКАЕТ ВЫЗОВ STATIC МЕТОДА БЕЗ СОЗДАНИЯ ОБЪЕКТА.

Правда они автоматически создаются (при компиляции) как глобальные с ограничением области видимости.

А вот теперь посмотрите к чему Ваш код может случайно привести:

int
main() {
Foo* mfoo=NULL; // Условно, мы забыли инициализровать mfoo.
Foo::set(mfoo);
Foo* l_foo = Foo::get();
l_foo->doSomething();
delete Foo::reset();
}

И это работает :) Я думаю комментарии излишни.

Повторюсь, я не против static переменных, но против static методов и вот одна из причин почему:

Источник http://citforum.nis.nnov.su/SE/project/pattern/p_2.shtml#3.3.2

Выдержка:
Разумнее создавать именно статический экземпляр специального класса, а не объявить требуемые методы статическими, поскольку при использовании методов экземпляра можно применить механизм наследования и создавать подклассы. Статические методы в языках программирования не полиморфны и не допускают перекрытия в производных классах.
Решение на основе создания экземпляра является более гибким, поскольку впоследствии может потребоваться уже не единственный экземпляр объекта, а несколько.
.
Я считаю, что в данном варианте это звучит так:

#include <iostream>
class A {
public:
A* instance() {return this;}
};

class B {
private:
static A* myA;
public:
B(){if (myA==NULL){ myA=new A; }; }
~B(){if (myA!=NULL){ delete myA; }; }
};

A* B::myA=NULL;

int
main() {
return 0;
}


В PHP так:

<?php

class A {
function instance() {return this;}
}

class B {
private static $myA=NULL;

function B() {
if ( is_null($myA) ) $myA = new A();
}

}

?>

По поводу Exception и конструкторов, почитайте :

http://uic.rsu.ru/doc/programming/c++/straustrup/cpptut.html
Бьерн Страуструп. Язык программирования С++
9.4.4 Особые ситуации и конструкторы
9.5 Особые ситуации могут не быть ошибками
 
Сверху