Как создать дочерний класс, используя его родительскую фабрику в __construct

У меня есть класс 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 в дочерних классах? Спасибо.

5

Решение

Вы пытаетесь использовать 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)

4

Другие решения

У вас есть небольшое недопонимание того, как наследование работает. Я думаю, что из-за этой строки в конструкторе 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 может рекурсивно понять, какие компоненты нужны вашим классам, создать их экземпляры и передать их конструкторам ваших классов.

3

По вопросам рекламы [email protected]