28

我在一个 Web 应用程序框架中工作,其中一部分由许多服务组成,所有服务都作为单例实现。它们都扩展了一个服务类,其中实现了单例行为,看起来像这样:

class Service {
    protected static $instance;

    public function Service() {
        if (isset(self::$instance)) {
            throw new Exception('Please use Service::getInstance.');
        }
    }

    public static function &getInstance() {
        if (empty(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

现在,如果我有一个像这样实现的名为 FileService 的类:

class FileService extends Service {
    // Lots of neat stuff in here
}

...调用 FileService::getInstance() 不会像我想要的那样产生一个 FileService 实例,而是一个 Service 实例。我认为这里的问题是服务构造函数中使用的“self”关键字。

还有其他方法可以在这里实现我想要的吗?单例代码只有几行,但我仍然希望尽可能避免任何代码冗余。

4

6 回答 6

61

代码:

abstract class Singleton
{
    protected function __construct()
    {
    }

    final public static function getInstance()
    {
        static $instances = array();

        $calledClass = get_called_class();

        if (!isset($instances[$calledClass]))
        {
            $instances[$calledClass] = new $calledClass();
        }

        return $instances[$calledClass];
    }

    final private function __clone()
    {
    }
}

class FileService extends Singleton
{
    // Lots of neat stuff in here
}

$fs = FileService::getInstance();

如果你使用 PHP < 5.3,也添加这个:

// get_called_class() is only in PHP >= 5.3.
if (!function_exists('get_called_class'))
{
    function get_called_class()
    {
        $bt = debug_backtrace();
        $l = 0;
        do
        {
            $l++;
            $lines = file($bt[$l]['file']);
            $callerLine = $lines[$bt[$l]['line']-1];
            preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function'].'/', $callerLine, $matches);
        } while ($matches[1] === 'parent' && $matches[1]);

        return $matches[1];
    }
}
于 2010-06-27T02:32:52.290 回答
9

如果我在 5.3 课上多加注意,我就会知道如何自己解决这个问题。使用 PHP 5.3 新的后期静态绑定特性,我相信 Coronatus 的提议可以简化为:

class Singleton {
    protected static $instance;

    protected function __construct() { }

    final public static function getInstance() {
        if (!isset(static::$instance)) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    final private function __clone() { }
}

我试过了,它就像一个魅力。不过,Pre 5.3 仍然是一个完全不同的故事。

于 2010-06-29T18:39:15.490 回答
3

这是固定约翰的答案。PHP 5.3+

abstract class Singleton
{
    protected function __construct() {}
    final protected function __clone() {}

    final public static function getInstance()
    {
        static $instance = null;

        if (null === $instance)
        {
            $instance = new static();
        }

        return $instance;
    }
}
于 2017-11-21T23:58:56.653 回答
3

我找到了一个很好的解决方案。

以下是我的代码

abstract class Singleton
{
    protected static $instance; // must be protected static property ,since we must use static::$instance, private property will be error

    private function __construct(){} //must be private !!! [very important],otherwise we can create new father instance in it's Child class 

    final protected function __clone(){} #restrict clone

    public static function getInstance()
    {
        #must use static::$instance ,can not use self::$instance,self::$instance will always be Father's static property 
        if (! static::$instance instanceof static) {
            static::$instance = new static();
        }
        return static::$instance;
    }
}

class A extends Singleton
{
   protected static $instance; #must redefined property
}

class B extends A
{
    protected static $instance;
}

$a = A::getInstance();
$b = B::getInstance();
$c = B::getInstance();
$d = A::getInstance();
$e = A::getInstance();
echo "-------";

var_dump($a,$b,$c,$d,$e);

#object(A)#1 (0) { }
#object(B)#2 (0) { } 
#object(B)#2 (0) { } 
#object(A)#1 (0) { } 
#object(A)#1 (0) { }

您可以参考 http://php.net/manual/en/language.oop5.late-static-bindings.php 了解更多信息

于 2018-06-19T06:52:21.500 回答
0

使用特征而不是抽象类允许扩展单例类。

将特征 SingletonBase 用于父单例类。

将特征 SingletonChild 用于其单例子项。

interface Singleton
{

    public static function getInstance(): Singleton;

}

trait SingletonBase
{

    private static $instance=null;

    abstract protected function __construct();

    public static function getInstance(): Singleton {

       if (is_null(self::$instance)) {

          self::$instance=new static();

       }

       return self::$instance;

    } 

    protected function clearInstance(): void {

        self::$instance=null;

    }

    public function __clone()/*: void*/ {

        trigger_error('Class singleton '.get_class($this).' cant be cloned.');
    }

    public function __wakeup(): void {

        trigger_error('Classe singleton '.get_class($this).' cant be serialized.');

    }

}

trait SingletonChild
{

    use SingletonBase;

}

class Bar
{

    protected function __construct(){

    }

}

class Foo extends Bar implements Singleton
{

      use SingletonBase;

}

class FooChild extends Foo implements Singleton
{

      use SingletonChild; // necessary! If not, the unique instance of FooChild will be the same as the unique instance of its parent Foo

}
于 2020-04-07T10:11:39.513 回答
0
    private static $_instances = [];

    /**
     * gets the instance via lazy initialization (created on first usage).
     */
    public static function getInstance():self
    {
        $calledClass = class_basename(static::class);

        if (isset(self::$_instances[$calledClass])) {
            self::$_instances[$calledClass] = new static();
        }

        return self::$_instances[$calledClass];
    }

这个唯一的问题是,如果你有同名的单身人士。

于 2020-12-06T14:31:09.273 回答