Могу ли я использовать абстрактный класс вместо закрытого __construct () при создании синглтона в PHP? - PullRequest
6 голосов
/ 28 марта 2010

При создании Singleton в PHP я гарантирую, что его невозможно создать, выполнив следующие действия:

class Singleton {

    private function __construct() {}
    private function __clone() {}

    public static function getInstance() {}
}

Однако я понял, что определение класса как «абстрактного» означает, что он не может быть создан. Так что нет ничего плохого в том, чтобы делать следующее:

abstract class Singleton {

    public static function getInstance() {}
}

Второй сценарий позволяет мне написать меньше строк кода, что было бы неплохо. (Не то, чтобы это действительно имело большое значение.)

Ответы [ 4 ]

10 голосов
/ 28 марта 2010

При создании синглтона в PHP, объявление __construct и __clone как частного гарантирует, что класс не может быть создан извне : он все еще может быть создан внутри его объявления.

При объявлении класса как abstract, он вообще не может быть создан ; даже изнутри его декларации.

Это означает, что ваше решение не будет работать: во втором случае ваш метод getInstance() не сможет создать экземпляр класса - в то время как он может сделать это в первом случае.

2 голосов
/ 28 марта 2010

Нет, потому что тогда вы вообще не сможете создать экземпляр класса (даже в статическом методе getInstance). Закрытый конструктор в примере синглтона просто гарантирует, что только статический метод getInstance из того же класса может обращаться к конструктору.

1 голос
/ 28 марта 2010

Нет, вы не можете использовать абстрактный класс вместо закрытого __construct () при создании синглтона. Но если вы намерены создать Abstract Singleton, из которого вы будете расширяться, вы можете сделать это следующим образом:

abstract class Singleton
{
   private static $_instances;
   public static function getInstance()
   {
      $className = get_called_class(); // As of PHP 5.3
      if(! isset(self::$_instances[$className] )) {
         self::$_instances[$className] = new $className();
      }
      return self::$_instances[$className];
   }
   protected function __construct( )  {}
   final private function __clone( )  {}
   final private function __wakeup( ) {}
}

Затем вы можете перейти от Singleton следующим образом:

class Foo extends Singleton {

    protected $_foo = 1;
    public function setFoo($i) { $this->_foo = $i; }
    public function getFoo() { return $this->_foo; }
}

и

class Bar extends Singleton {

    protected $_foo = 1;
    public function setFoo($i) { $this->_foo = $i; }
    public function getFoo() { return $this->_foo; }
}

и манипулирование:

$foo1 = Foo::getInstance();
$foo1->setFoo(5);

$foo2 = Foo::getInstance();
var_dump($foo2); 

$bar1 = Bar::getInstance();
var_dump($bar1);

echo new ReflectionObject($foo2);
echo new ReflectionObject($bar1);

Однако имейте в виду, что одиночные тесты очень сложны для юнит-тестирования, и их следует по возможности избегать. Смотрите мой ответ здесь для некоторого фона:

0 голосов
/ 28 марта 2010

Это может сработать, если ваш Singleton::getInstance() должен вернуть экземпляр другого класса.

abstract class Singleton {
  public static function getInstance() {
    static $instance = null;
    if ( is_null($instance) ) {
      $instance = new StdClass; // a different class than 'abstract class Singleton'
      $instance->x = time();
    }
    return $instance;
  }
}

$obj = Singleton::getInstance();

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

...