【问题标题】:PHP Using Factory pattern for SDKs用于 SDK 的 PHP 使用工厂模式
【发布时间】:2015-10-09 18:35:55
【问题描述】:

我在这里有点迷茫,因为我想做一些在 Java 中非常简单但在 PHP 中似乎有点复杂的事情。

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

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

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

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

【问题讨论】:

    标签: php design-patterns


    【解决方案1】:

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

    然而,在 PHP 7 中,这将发生变化。

    仍然没有嵌套类(尽管我们将来可能会得到它们,请参阅:https://stackoverflow.com/a/31454435/664108),但您可以实例化一个 anonymous class 并只为消费者提供如下接口:

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

    【讨论】:

      【解决方案2】:

      目前还没有本地方法可以做到这一点。但是,如果您真的想“强制”您的类仅从您的工厂类创建,则有一些“hackish”方法可以通过实例化类来限制实例化。

      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
      

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

      【讨论】:

      • 它很有创意,但我认为 debug_backtrace 以“debug”开头是有原因的,你应该只将它用于调试或非常特定的用例。在我看来,这两者都不是。这是不好的做法,会导致代码不可读。
      • @l.renkema 正如我在帖子中提到的,这是一种“骇人听闻”的方式。我相信,这个案例已经足够具体了,由 OP 来决定。此外,这与代码可读性无关 - 它对我来说非常清楚。
      【解决方案3】:

      正如其他人所说,目前在 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() 调用它。这样你就可以像构造函数是公共的那样使用依赖注入。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多