Blocking the possibility to create classes directl

2019-06-22 06:01发布

问题:

In a base class for all the models in our MVC system, I created a factory method BaseCLass::getNew() that returns an instance of the requested child class when called via SomeChildClass::getNew().

Now, I'm looking for a way to force the programmer to use this factory. I.e., idially I'd like that any class created directly, like this:

 new SomeChildClass

will throw an exception upon creation, and only classes created by the factory will be usable.

Any ideas how can this be achieved?

Our code is written in PHP, but good chance that your idea will be valuable even if you think on a different language.

edit: I cannot make my constructor private, as the framework constructor in the class that I inherit is public, and php would not allow me this.

回答1:

By making the class have a private constructor.

Update -- solution that covers your stated requirements

class Base {
    private static $constructorToken = null;

    protected static function getConstructorToken() {
        if (self::$constructorToken === null) {
            self::$constructorToken = new stdClass;
        }

        return self::$constructorToken;
    }
}

class Derived extends Base {
    public function __construct($token) {
        if ($token !== parent::getConstructorToken()) {
            die ("Attempted to construct manually");
        }
    }

    public static function makeMeOne() {
        return new Derived(parent::getConstructorToken());
    }
}

This solution takes advantage of the object equality rules for stdClass by storing a "magic password" object on the base class which only derived classes can access. You can tweak it to taste.

I wouldn't call it horrible like the debug_backtrace idea, but still I have the impression that things should be done differently.



回答2:

By making the constructor of the child class protected. The parent class will have access to all protected methods of the child. Any attempt to directly create the child (ie: new child) will cause a fatal error.

<?php

class factory
{

    static public function create()
    {
        return new child;
    }

}

class child extends factory
{

    protected function __construct()
    {
        echo 'Ok';
    }

}

$c = factory::create(); // Ok
$c2 = new child; // fatal error

?>

Though this method won't let you throw an exception instead :(

If then absolutely necessary, only debug_backtrace() function comes to mind (besides using singleton for the child itself, or forced object pool patterns using and passing GUID's generated by factory and verified by child). Within the child constructor, look at the 2nd array value to make sure "function" === "create" and "class" === "factory. Throw exception if not matching. I didn't suggest this initially, only because I suspect using debug_backtrace may give a performance hit.



回答3:

Declare the class's constructor private, and it can only be called from within the class's own methods like getNew().



回答4:

there are couple of ways to implement it

  • make parent class private use magic
  • user magic function __autoload; check the type of class and through error with not allowed message

http://php.net/manual/en/function.is-a.php