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.
一样使用依赖注入
如前所述。那种方式是单一的气味。通过创建一个对象而不调用它的构造函数,没有真正的方法可以在创建时验证对象状态
这是一个概念证明,但这个概念很烂。
我在这里有点迷路,因为我想做一些在 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.
如前所述。那种方式是单一的气味。通过创建一个对象而不调用它的构造函数,没有真正的方法可以在创建时验证对象状态
这是一个概念证明,但这个概念很烂。