Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instantiate a child class using its parent factory in __construct

Tags:

oop

php

factory

I have a Class A which must be instantiated using other multiples objects

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

To save me the trouble of this class instantiation, I have set up a factory for this class.

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

I have a class B with extends class A. My problem is that I can't figure out how to instantiate the A class within the B class in order to access the property 'foo'.

It felt natural to me to try:

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

but it generates a notice error when trying to access the object A properties:

Undefined property: A::$foo

How can I use the class A factory to instantiate A easily within its child classes? Thank you.

like image 862
Vincent Avatar asked Sep 12 '15 18:09

Vincent


People also ask

Can a child class be instantiated as a parent?

Following the object oriented methodology, a child class can be instantiated as an object that can inherit the attributes of the parent class.

What are the __ construct () and __ destruct () methods in a PHP class?

Example# __construct() is the most common magic method in PHP, because it is used to set up a class when it is initialized. The opposite of the __construct() method is the __destruct() method. This method is called when there are no more references to an object that you created or when you force its deletion.

What is parent __construct?

We can do this by using the special function call parent::__construct(). The "parent" part means "get the parent of this object, and use it", and the __construct() part means "call the construct function", of course. So the whole line means "get the parent of this object then call its constructor".


2 Answers

You try to use A_Factory::create_A() in the same way as you would use parent::__construct(). However, these are two completely different calls.

parent::__construct()

parent resolves to A. The current object $this is an instance of A (because every instance of B is also an instance of A due to inheritance). In this case, the call is not a static call, although the operator :: has been used ($this stays the same).

The following code works:

(given that foo is not private)

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

This one works too:

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

Factory

A_Factory::createA() is a static call, because A_Factory is not in the inheritance tree of B. Also A_Factory creates and returns a new instance of A. So as soon as this has been called, you have two different objects: $this is still the unchanged B instance and you created a different instance of A without assigning it to any variable.

A possible approach is to move the factory method into A itself.

This will work:

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();

This makes use of late static binding with the static keyword. static resolves to the class name of the called class, so that it is A in A::create() and B in B::create().

Note the difference to self, which always resolves to the class where the method is declared (in this case it would be always A)

like image 77
Fabian Schmengler Avatar answered Oct 05 '22 00:10

Fabian Schmengler


You have a slight misunderstanding of how inheritance works. I think that because of that line in B's constructor:

A_Factory::create_A();

The parent is not a property of the child class. If you create a new A([...]) within B's constructor, it will be an other class, completely separated from your B, and there is no way to "merge" it to an existing object. To construct the parent from a child class, you would do that:

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

Here is a way of creating your classes, using a factory and some type-hinting. Notice how I moved the different new XYZ() so that they are not in your classes constructors. Naked news in constructors is somewhat considered bad practice as it hides the dependencies of your classes.

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);

The real answer: you want your factory to be a dependency injection container, such as Auryn, and by using type-hints your classes would be recursively created with their dependencies, using reflection.

in your case (adapted from an exemple in auryn's 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);

Using reflection, Auryn can recursively understand what are the components your classes need, instantiate them and pass them to your classes constructors.

like image 29
Félix Adriyel Gagnon-Grenier Avatar answered Oct 04 '22 23:10

Félix Adriyel Gagnon-Grenier