Search code examples
phpoopinheritancedependency-injectionfactory-pattern

PHP - Mixing object Injection & inheritance


This is a follow up question on the following answer : Parent Object in php

class A {
    protected function doSomeStuff(){
       echo 'a method that all children will need to call';
    }
}

class B {
    protected $_parent;
    public function __construct($parent) {
       $this->_parent = $parent;
    }
    public function doSomeLocalStuff() {
       $this->_parent->doSomeStuff(); // Fatal Error
    }
}

$a = new A(); // will be used for other children as well.
$b = new B($a);
$b->doSomeLocalStuff();

In the above code, parent object Injection was used, allowing class B to be initialized using a specific instance of class A, but class B wont be able to access class A protected properties or methods (e.g., doSomeStuff()).

But by mixing the above with inheritance, we get the best of both worlds :)

class B extends A {
    protected $_parent;
    public function __construct($parent) {
       $this->_parent = $parent;
    }
    public function doSomeLocalStuff() {
       $this->_parent->doSomeStuff(); // Works :)
    }
}

So, is this acceptable ? .. any drawbacks ?

P.S: I'm trying to implement a non-static factory pattern.

Clarification

Consider this, I'm trying to design a class which will be used for calling an external API. We've over 400 different calls, divided into 10 categories (billing, customers, products ... ).

All the 400 calls shares the same parent-url, username/password and some other common properties.

So, instead of putting the 400 method in one big class, I decided to divide them into 10 classes, with a parent class handling common functions (e.g., authentication, url construction, web call ... ), then created a factory pattern, where I can load only needed classes/categories on run-time.

Something like :

$apiCall = new parentPlusFactory(); 
          //contains common methods and a mechanism to load sub-classes
$apiCall->setAPIuserName("user");
$apiCall->setAPIpassword("pass");
$apiCall->useClass('customers')->doSomeCustomerStuff();
$apiCall->useClass('products')->doSomeProductStuff();

That's why I need to share the same parent class instance.


Solution

  • In general you should favor composition over inheritance. To me your use case sounds like B should not be extending A at all, but instead you should have two separate classes.

    Now, PHP 5.4 will have "horizontal reuse", also known as "traits", where it will be possible to "include" a trait into your class.

    trait A
    {
        public function doSomeStuff()
        {
            echo 'doing some stuff';
        }
    }
    
    class B
    {
        use A;
    
        public function doSomeLocalStuff()
        {
            $this->doSomeStuff();
        }
    }
    
    class C
    {
        use A;
    
        public function doSomeLocalStuff()
        {
            echo 'Doing something completely different here';
        }
    }
    

    See also PHP manual: traits and PHP 5.4 beta1 released.