PHP使用SDK的工厂模式

时间:2015-07-20 15:02:11

标签: php design-patterns

我在这里有点迷失,因为我想在Java中做一些非常简单但在PHP中看起来有点复杂的东西。

我们正在为我们的产品和Java构建SDK,我们有一个类,不能(!)由用户(即编码器)实例化,因为它有关于它的完整性的若干约束。所以我们在“XFactory”中构建了一个嵌套类“X”,你将通过调用XFactory.buildMeMyX()得到一个X实例; - 容易......

现在PHP根本不支持嵌套类,我想知道如何在这里应用相同的类。在Java中,X的构造函数是隐藏的(私有),因此只有XFactory可以调用它。

在PHP中,看起来我必须公开__construct()并将嵌套的类X移出XFactory。因此,用户将能够在没有Factory的情况下创建实例。

现在 - 我可以将工厂功能移到X本身并移动所有东西,但这会破坏SDK的设计。毕竟,有没有一种有用的方法在PHP中做这些事情?

3 个答案:

答案 0 :(得分:3)

对于 PHP 5.x ,您已经描述了您的选项,根本没有私有/受保护的类或内部类,因此没有其他方法可以限制实例化。

然而,使用PHP 7,这将会改变。

仍然没有嵌套类(虽然我们将来可能会看到它们,请参阅:https://stackoverflow.com/a/31454435/664108),但您可以实例化anonymous class并仅向消费者提供如下所示的接口:< / p>

class XFactory
{
    public function buildMeMyX()
    {
        return new class() implements XInterface {
            public function doWhatEverAnXCanDo()
            {
                // X X X
            }
            // ...
        };
    }
}
interface XInterface
{
    function doWhatEverAnXCanDo();
}

答案 1 :(得分:1)

还没有本地方法可以这样做。但是,如果你真的想要强制执行&#34;你的班级只是从你的工厂班级创建的,有一点&#34; hackish&#34;通过异步类来限制实例化的方法。

class X
{

    function __construct()
    {
        new Y();
    }
}

class Y
{
    function __construct()
    {
        $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);

        if (!isset($trace[1]['object']) || !($trace[1]['object'] instanceof X)) {
            throw new \RuntimeException('This is a private class');
        }
    }
}

new X(); // All is fine

new Y(); // Exception

请注意,没有&#34;真实&#34;即使使用这种方法,保护类不被其他地方实例化的方法 - 它仍然可以通过绕过构造函数或仅修改源来通过反射来完成。

答案 2 :(得分:1)

正如其他人所说,目前在PHP中没有干净的方法来实现这种行为。在我看来,私有构造函数的唯一有效用例是实现工厂的类中的工厂

每当你试图绕过那个用例时,它就会变得混乱。 没有人应该尝试发明巧妙的方法来绕过PHP的语言限制。

我只是为了证明确实可能而违反了这条规则。但请不要在生产中使用它,或者更好:在任何地方使用它。我将尝试为该建议找到一些防弹参数并在之后编辑答案。

<?php

class Dependency {}

class SomeClass {

    protected $dep;

    private function __construct(Dependency $dep) 
    {
        $this->dep = $dep;
    }

    public function doSomething()
    {
        var_dump($this->dep);
        echo "Doing Stuff and even having dependencies";
    }

}

class SomeClassFactory {

    public function buildSomeClass()
    {
        return $this->instantiateSomeClassWith(new Dependency);
    }

    protected function instantiateSomeClassWith()
    {
        $reflectionClass = new ReflectionClass('SomeClass');
        $someClass = $reflectionClass->newInstanceWithoutConstructor();

        $constructor = $reflectionClass->getConstructor();
        $constructorClosure = $constructor->getClosure($someClass);
        call_user_func_array($constructorClosure, func_get_args());
        return $someClass;
    }

}

$factory = new SomeClassFactory();
$someClass = $factory->buildSomeClass();
$someClass->doSomething();

?>

输出:object(Dependency)#2 (0) { } Doing Stuff and even having dependencies

理论很简单。将通过Factory构建的类的构造函数是私有的。我们在工厂中使用反射来创建类的实例而不调用构造函数。

一旦我们有了一个实例,我们抓住构造函数的闭包并通过call_user_func_array()调用它。这样就可以像使用构造函数一样使用依赖注入。

正如我之前所说。那种方式是一种气味。通过创建一个对象而不调用它的构造函数,没有真正的方法来在创建时验证对象状态

这是一个概念证明,但概念很糟糕。