Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instantiate a child singleton of an abstract class?

I have an abstract class and a child that extends the abstract class. The child is supposed to be a sigleton. Here is simplified example of the abstract class:

abstract class AbstractClass{
    protected static $instance = NULL;
    abstract protected function meInit();

    private function __construct(){
        $this->meInit();
        $this->init();
    }

    private function __clone(){}

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

    function init(){
        'code here;

    }
}

Here is simplified child class:

class ChildClass_A extends AbstractClass{

    protected function meInit(){
        'some code;
    }
}

When I try to get an instance of the child $child = ChildClass_A::getInstance(); I get this error:

Fatal error: Cannot instantiate abstract class AbstractClass in C:\wamp\www\Classes\AbstractClass.php on line 7

I suspect the culprit is in self::$instance=new self;. How should I redo it to achieve what I need?

like image 655
user2395238 Avatar asked Jan 09 '23 08:01

user2395238


1 Answers

You're almost there; you just can't use new self() like this because it's trying to do a new A(). Instead, use get_called_class() so that a new B is created instead.

// ONLY SUPPORTS ONE SUBCLASS
// KEEP READING BELOW FOR COMPLETE SOLUTION
abstract class A {

    static protected $instance = null;

    abstract protected function __construct();

    static public function getInstance() {
        if (is_null(self::$instance)) {
            $class = get_called_class();
            self::$instance = new $class();
        }
        return self::$instance;
    }

}

class B extends A {
    protected function __construct() {
        echo "constructing B\n";
    }
}

var_dump(B::getInstance()); // constructing B, object(B)#1 (0) {}
var_dump(B::getInstance()); // object(B)#1 (0) {}

OK, but what happens now when we try to make another subclass?

class C extends A {
    protected function __construct() {
        echo "constructing C\n";
    }
}

var_dump(C::getInstance()); // object(B)#1 (0) {}
var_dump(C::getInstance()); // object(B)#1 (0) {}

Well that sucks! I wanted a C instance, not the B one! This is because the abstract class A is only saving one instance. We have to make it support one of each subclass.

Well that's easy!

// SOLUTION:
// WORKS FOR MULTIPLE SUBCLASSES
abstract class A {

    static protected $instances = array();

    abstract protected function __construct();

    static public function getInstance() {
        $class = get_called_class();
        if (! array_key_exists($class, self::$instances)) {
            self::$instances[$class] = new $class();
        }
        return self::$instances[$class];
    }

}

Class B and C can stay the same ...

class B extends A {
    protected function __construct() {
        echo "constructing B\n";
    }
}

class C extends A {
    protected function __construct() {
        echo "constructing C\n";
    }
}

Now let's check out how they behave

var_dump(B::getInstance()); // constructing B, object(B)#1 (0) {}
var_dump(B::getInstance()); // object(B)#1 (0) {}

var_dump(C::getInstance()); // constructing C, object(C)#2 (0) {}
var_dump(C::getInstance()); // object(C)#2 (0) {}

Oh good! Just what we always wanted!

like image 169
Mulan Avatar answered Jan 20 '23 21:01

Mulan