PHP中使用Singleton Pattern的类继承仅在继承类中的实例变量重新初始化时才有效。但为什么?

时间:2017-08-04 07:12:18

标签: php class inheritance singleton

我有一个主要类,其中包含单例函数 instance()和关联变量 $ instance 。现在我创建了几个子类,让主类继承。由于有用的继承,我没有重新定义单例函数和变量。不幸的是,每个实例都指向第一个子类。只有在子类中 $ instance 变量初始化为 null 时才有效,但为什么呢?使用关键字静态而非自我,范围应保留在子类中。

以下是更好地理解我的意思的源代码:

// PHP Version 7.0
// Don't work as expected:

class base1
{
    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null;

    /*
     * For Singleton Pattern
     */
    public static function instance() {

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

        return static::$instance;
    }

    public function test()
    {
        $test = "base1";
        var_dump($test);
    }
}

class sub11 extends base1
{
    public function test()
    {
        $test = "base1 -> sub11";
        var_dump($test);
    }
}

class sub12 extends base1
{
    public function test()
    {
        $test = "base1 -> sub12";
        var_dump($test);
    }
}

$sub11 = sub11::instance();
$sub12 = sub12::instance();
$sub11->test();
$sub12->test();
// Output:
// string(14) "base1 -> sub11"
// string(14) "base1 -> sub11"  // It's not different!!!

// Work as expected:

class sub21 extends base1
{

    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null;  // Is needed, but why?

    public function test()
    {
        $test = "base1 -> sub21";
        var_dump($test);
    }
}

class sub22 extends base1
{
    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null; // Is needed, but why?

    public function test()
    {
        $test = "base1 -> sub22";
        var_dump($test);
    }
}

$sub21 = sub21::instance();
$sub22 = sub22::instance();
$sub21->test();
$sub22->test();
// Output:
// string(14) "base1 -> sub21"
// string(14) "base1 -> sub22"  // This is correct !

1 个答案:

答案 0 :(得分:3)

在您的第一部分中,它正在按照预期正确正确。两个类 sub11 sub12 使用 base1 的字段来存储实例。因此,第一个被实例化的,放在那里,防止其他人覆盖已经创建的实例。

在第二部分中,您为每个后代类指定了个人静态存储字段,因此它们不使用基类字段,而是使用自己的字段(它与父字段重叠,因为使用相同的名称)。

简而言之,第一对后代类使用base1::$instance来检查和创建实例。第二对使用自己的字段sub21::$instancesub22::$instance来执行此任务。

您可以通过放弃 base1 类中的后期静态绑定来阻止这种情况:

class base1
{
    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null;

    /*
     * For Singleton Pattern
     */
    public static function instance() {
        if ( null == self::$instance ) {
        //           ^ self instead of static
            self::$instance = new static();
        //  ^ self instead of static
        }

        return self::$instance;
        //     ^ self instead of static
    }

    public function test()
    {
        $test = "base1";
        var_dump($test);
    }
}

我确实建议您阅读late static binding以及selfstatic个关键字之间的区别。

<强> UPDv1:

如果您仍然只需要获取每个后代类的一个实例,则可以将单例方法更改为:

class base1
{
    /*
    * Instances of descendant classes
    * array
    */
    protected static $instances = [];

    /*
     * For Singleton Pattern
     */
    public static function instance() {
        if (empty(self::$instances[static::class])) {
            $instance = new static();

            self::$instances[static::class] = $instance;
        } else {
            $instance = self::$instances[static::class];
        }

        return $instance;
    }

    public function test()
    {
        $test = "base1";
        var_dump($test);
    }
}