PHP 为 SDK 使用工厂模式

PHP Using Factory pattern for SDKs

我在这里有点迷路,因为我想做一些在 Java 中很容易但在 PHP 中似乎有点复杂的事情。

我们正在为我们的产品构建一个 SDK,在 Java 中,我们有这个 class 不能(!)由用户(即编码器)实例化,因为有关于它的完整性的几个限制。因此,我们将其构建为 "XFactory" 内部的嵌套 class "X",您将通过调用 XFactory.buildMeMyX(); 获得 X 的实例; - 简单...

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

在 PHP 中,看起来我必须制作 __construct() public 并将嵌套的 class X 移出 XFactory。因此,用户将能够在没有工厂的情况下创建实例。

现在 - 我可以将工厂功能移动到 X 本身并将所有内容移动到那里,但这会破坏 SDK 的设计。毕竟 PHP 有做这些事的有用方法吗?

对于PHP5.x您已经描述了您的选择,没有private/protected类或内部类,所以没有进一步的方法来限制实例化。

但是,随着 PHP 7 的出现,这种情况将会改变。

仍然没有嵌套类(尽管我们将来可能会得到它们,请参阅:), but you could instantiate an anonymous class并且只向消费者提供这样的接口:

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

目前还没有本地方法可以做到这一点。然而,如果你真的想要 "enforce" 你的 class 只是从你的工厂 class 创建的,有一些 "hackish" 方法可以通过实例化 "hackish" 来限制实例化 class。

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

请注意,即使使用这种方法,也没有 "real" 方法来保护 class 不被从其他地方实例化 - 它仍然可以通过绕过构造函数的反射来完成,或者简单地修改你的来源。

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

每当您尝试绕过该用例时,它就会变得一团糟。 任何人都不应尝试发明巧妙的方法来绕过 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

理论很简单。将通过工厂构建的 class 的构造函数设为私有。我们利用工厂内的反射来创建 class 的实例,而无需调用构造函数。

一旦我们有了一个实例,我们就获取构造函数的闭包并通过 call_user_func_array() 调用它。这样你就可以像构造函数 public.

一样使用依赖注入

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

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