我有一个必须使用其他多个对象实例化的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?谢谢。
答案 0 :(得分:4)
您尝试使用A_Factory::create_A()
的方式与使用parent::__construct()
的方式相同。但是,这是两个完全不同的电话。
parent
解析为A
。当前对象$this
是A
的实例(因为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
实例,而不将其分配给任何变量。< / p>
一种可行的方法是将工厂方法移动到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 binding与static
关键字结合使用。 static
解析为被调用类的类名,因此A
中的A::create()
和B
中的B::create()
。
请注意self
的差异,它总是解析为声明的方法的类(在这种情况下,它总是A
)
答案 1 :(得分:3)
您对继承的工作原理略有误解。我认为因为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,并且通过使用类型提示,您的类将使用其依赖项递归创建,使用reflection
在你的情况下(改编自auryn的github repo中的例子):
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可以递归地理解你的类需要什么组件,实例化它们并将它们传递给类构造函数。