【问题标题】:How to instantiate a child class using its parent factory in __construct如何在 __construct 中使用其父工厂实例化子类
【发布时间】:2015-09-12 18:36:07
【问题描述】:

我有一个必须使用其他多个对象实例化的 A 类

class A{
   function __construct(new X(), new Y(), new Z()){
      $this->foo = 'foo';
   }
}

为了省去我这个类实例化的麻烦,我为这个类建立了一个工厂。

class A_Factory{
    public static function create_A(){
        return new A(new X(), new Y(), new Z());
    }
}

我有一个扩展类 A 的 B 类。我的问题是我不知道如何在 B 类中实例化 A 类以访问属性“foo”。

对我来说尝试是很自然的:

class B extends A{
   function __construct(){
      A_Factory::create_A();  
   }
}

但在尝试访问对象 A 属性时会产生通知错误:

Undefined property: A::$foo

如何使用 A 类工厂在其子类中轻松实例化 A?谢谢。

【问题讨论】:

  • 父::__construct ?
  • 工厂的重点是不要在A的实例化中使用construct以及所需对象的列表
  • 你确定这是一个好的设计吗?
  • 如果类是继承的,我不明白为什么要创建实例
  • 如果B扩展A,它继承foo,所以只需使用$this->foo。除非foo 只设置在A 的构造函数中,你在B 中重载,在这种情况下你需要先调用parent::__construct()

标签: php oop factory


【解决方案1】:

您尝试像使用parent::__construct() 一样使用A_Factory::create_A()。然而,这是两个完全不同的调用。

父::__construct()

parent 解析为 A。当前对象$thisA的一个实例(因为B的每个实例也是A的一个实例由于继承)。在这种情况下,调用不是静态调用,尽管使用了运算符::$this 保持不变)。

以下代码有效:

(假设foo 不是private

class B extends A
{
    function __construct()
    {
        parent::__construct(new X, new Y, new Z);
        echo $this->foo;
    }
}

这个也可以:

class B extends A
{
    function __construct()
    {
        A::__construct(new X, new Y, new Z);
        echo $this->foo;
    }
}

工厂

A_Factory::createA() 是一个静态调用,因为A_Factory 不在B 的继承树中。 A_Factory 创建返回A的新实例。因此,一旦调用了它,您就有两个不同的对象:$this 仍然是未更改的 B 实例,并且您创建了一个不同的 A 实例而不将其分配给任何变量。

一种可能的方法是将工厂方法移动到A 本身。

这会起作用:

class A
{
    function __construct(X $x, Y $y, Z $z)
    {
        $this->foo = 'foo';
    }
    public static function create()
    {
        return new static (new X, new Y, new Z);
    }
}

class B extends A
{
}

// Instantiating:
$a = A::create();
$b = B::create();

这利用了late static bindingstatic 关键字。 static解析为被调用类的类名,所以在A::create()中为A,在B::create()中为B

注意与self 的区别,它总是解析到方法被声明的类(在这种情况下,它总是A

【讨论】:

  • 如果我A 有多个子类,例如BCDE 等,则在每个子类中声明parent::__construct(new X, new Y, new Z); 将难以编码如果A 的构造在未来发生变化,则进行维护。那样的话,你会推荐使用你提到的工厂方法吗?
  • 是的,我推荐它。除非对象创建变得更加复杂和多样化,否则使用 Abstract FactoryBuilder 之类的模式将实例化与类本身分离会再次得到回报。
【解决方案2】:

您对继承的工作原理有一点误解。我认为是因为 B 的构造函数中的那一行:

A_Factory::create_A();

父类不是子类的属性。如果您在 B 的构造函数中创建 new A([...]),它将是另一个类,与您的 B 完全分离,并且无法将其“合并”到现有对象。要从子类构造父类,您可以这样做:

class B {
    function __construct() {
        parent:__construct(new X(),new Y(),new Z());
    }
}

这是一种创建类的方法,使用工厂和一些类型提示。请注意我是如何移动不同的new XYZ() 以使它们不在您的类构造函数中。构造函数中的裸新闻在某种程度上被认为是不好的做法,因为它隐藏了类的依赖关系。

class B_Factory {
    public static function create_B(X $X,Y $Y, Z $Z) {
        return new B($X, $Y, $Z);
    }
}

class X {}
class Y {}
class Z {}

class A {
    public function __construct(X $X, Y $Y, Z $Z) {
        $this->foo = "foo";
    }
}

class B extends A {
    public function __construct(X $X, Y $Y, Z $Z) {
        parent::__construct($X,$Y,$Z);
    }
}

$B = B_Factory::create_B(new X(), new Y(), new Z());
var_dump($B->foo);

真正的答案:您希望您的工厂是一个依赖注入容器,例如Auryn,并且通过使用类型提示,您的类将使用它们的依赖关系递归创建,使用@987654322 @。

在您的情况下(改编自 auryn 的 github 存储库中的示例):

class X {}
class Y {}
class Z {}

class A {
    public function __construct(X $X, Y $Y, Z $Z) {
        $this->foo = "foo";
    }
}

class B extends A {
    public function __construct(X $X, Y $Y, Z $Z) {
        parent::__construct($X, $Y, $Z);
    }
}

$injector = new Auryn\Injector;
$B = $injector->make('B');
var_dump($B->foo);

使用反射,Auryn 可以递归地了解您的类需要哪些组件,实例化它们并将它们传递给您的类构造函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多