У меня есть класс 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());
}
}
У меня есть класс B с расширением класса A. Моя проблема в том, что я не могу понять, как создать экземпляр класса A в классе B для доступа к свойству ‘foo’.
Для меня было естественным попробовать:
class B extends A{
function __construct(){
A_Factory::create_A();
}
}
но он генерирует ошибку уведомления при попытке доступа к свойствам объекта A:
Undefined property: A::$foo
Как я могу использовать фабрику класса A, чтобы легко создать экземпляр A в дочерних классах? Спасибо.
Вы пытаетесь использовать 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
без присвоения его какой-либо переменной.
Возможный подход — перенести фабричный метод в 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();
Это использует поздняя статическая привязка с static
ключевое слово. static
разрешается в имя класса вызываемого класса, так что это A
в A::create()
а также B
в B::create()
,
Обратите внимание на разницу self
, который всегда разрешает класс, где метод объявленный (в этом случае это будет всегда A
)
У вас есть небольшое недопонимание того, как наследование работает. Я думаю, что из-за этой строки в конструкторе B:
A_Factory::create_A();
Родитель не является свойством дочернего класса. Если вы создаете new A([...])
в конструкторе B это будет другой класс, полностью отделенный от вашего 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, и с помощью подсказок типа ваши классы будут рекурсивно созданы с их зависимостями, используя отражение.
в вашем случае (адаптировано из примера в репозитории auryn’s 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 может рекурсивно понять, какие компоненты нужны вашим классам, создать их экземпляры и передать их конструкторам ваших классов.