php - 仅当重新初始化继承类中的实例变量时,PHP 中使用单例模式的类继承才有效。但为什么?

标签 php class inheritance singleton

我有一个带有单例函数 instance() 和关联变量 $instance 的主类。现在我创建几个子类并让主类继承。我没有重新定义单例函数和变量,因为继承很有用。不幸的是,每个实例都指向第一个子类。只有在子类中 $instance 变量被初始化为 null 时它才起作用,但为什么呢?使用关键字 static 而不是 self 时,作用域应保留在子类中。

这里是源代码,可以更好地理解我的意思:

// 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 !

最佳答案

在您的第一部分中,它工作正确并且符合预期。 sub11sub12 类都使用 base1 的字段来存储实例。因此,第一个已启动的实例被放置在那里并防止其他人覆盖已经创建的实例。

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

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

您可以通过丢弃 base1 类中的后期静态绑定(bind)来防止这种情况:

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:

如果您仍然需要仅获取每个后代类的一个实例,您可以将 singleton 方法更改为如下所示:

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);
    }
}

关于php - 仅当重新初始化继承类中的实例变量时,PHP 中使用单例模式的类继承才有效。但为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45500377/

相关文章:

java - Java中的继承[子类声明为 protected 时出错]

派生类的c++ vector 仅调用一次构造函数

php - 如何安排 cron 作业每天提取数据库并通过邮件发送报告

php - 如何在最大程度地减少重复的同时使用客户端和服务器端验证?

javascript - ckeditor 将 html 内容显示为纯文本

python - 如何在Python for循环中创建和删除类实例

ruby-on-rails - 在 Rails 控制台中定义一个类(实例)方法

php - 如何检查数组(php)中的任何项目是否在表(mysql)中

javascript - 一个类的所有链接 - 如何在点击时添加其他类?

c++ - 没有虚拟时应该使用私有(private)继承吗?