5

I have two classes

ClassA , ClassB

Classes commonly depend upon two basic services and repositories

ServiceA , ServiceB

Classes (ClassA , ClassB) use DI principle to inject dependency using constructor.

Since all three share a few common service as mentioned above I want to group all the common methods and services to a class Base Like this

Base Class

class Base {

    protected $A;
    protected $B;

    public function __construct(ServiceA $A, ServiceB $B){
       $this->A = $A;
       $this->B = $B;
    }
}

Child Service

class Child extends Base {

    protected $C;        

    public function __construct(ChildDependency $C){
       $this->C = $C;
    }

    public function doStuff()
    {
         //access the $A (**Its null**)
         var_dump($this->A);
    }

}

Question

How can I have common parent dependency without breaking IoC principles?

Possible Case 1

I know I have to call parent::__construct() to initialize Base constructor. But then I have to define Parent's dependency in all child class like below.

(But for large number of child I have to repeat this process. It defeats purpose of having common DI point).

class Child extends Base {

    protected $C;        

    public function __construct(ChildDependency $C, ParentDepen $A, ParentDepn $B){
       parent::_contruct($A,$B);
       $this->C = $C;
    }
}

Possible Case 2

Having use Getter and Setter. But I think they break the IoC principle.

halfer
  • 19,824
  • 17
  • 99
  • 186
Sushant
  • 1,354
  • 1
  • 14
  • 31
  • Could you please share you're actual use case with us? I believe that depending what `Base` `Child` and the dependencies actually are there might be different ways to solve this problem. – lukasgeiter Feb 22 '15 at 22:14
  • 1
    This sounds like you're breaking [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle) (and maybe some other [SOLID](http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29) principles), as well as having [lasagna code](http://en.wikipedia.org/wiki/Spaghetti_code#Lasagna_code). – Jasper N. Brouwer Feb 22 '15 at 22:45
  • `ClassA , ClassB` Where are they? – sectus Feb 24 '15 at 10:06
  • @sectus they are are in same folder. But not limited to be here. – Sushant Feb 24 '15 at 11:07
  • @JasperN.Brouwer , the can you post a good example to achieve this. – Sushant Feb 24 '15 at 11:08
  • @lukasgeiter Actual use case is not so much different. I have a some base classes that are always used in childs . Plus i have different dependencies that related to each child class. I have to include Base class dependency in child always to I can pass it to parent::constructor(). Is there a way so I have to inject parent dependency from single file. – Sushant Feb 24 '15 at 11:11
  • 2
    I need way more context to determine where possible desing flaws are located. What is the concrete problem you are trying to solve? – Jasper N. Brouwer Feb 24 '15 at 15:13
  • You could consider using Interface Injection (and Traits to implement those interfaces) as opposed to Constructor Injection. However, as everyone else has already said (e.g. Jasper), it's impossible to help you without a real-world use case; as described, it smells like you're cooking spaghetti. – Gabriel Feb 24 '15 at 21:21

3 Answers3

4

This seems like your best bet if you have to inject your dependency from whatever is creating your object:

class Child extends Base {

    protected $C;        

    public function __construct(ServiceA $A, ServiceB $B, ChildDependency $C){
       parent::__contruct($A, $B);
       $this->C = $C;
    }
}

You could try using Traits instead:

trait ServiceATrait {
    public $A = new ServiceA();
    public function getServiceA() { return $this->A; }
}
trait ServiceBTrait {
    public $B = new ServiceB();
    public function getServiceB() { return $this->B; }
}
class Base {
    use ServiceATrait;
    use ServiceBTrait;
}
class Child extends Base {
    protected $C;        

    public function __construct(ChildDependency $C) {
       $this->C = $C;
    }
}

function() {
    $c = new Child(new ChildDependency());
    echo $c->getServiceB()->toString();
}
Derek
  • 4,575
  • 3
  • 22
  • 36
  • Thanks, but as I mentioned in scenario 1 , if i have large number of classes and later on if I want to change dependency I have to edit all child classes. – Sushant Feb 07 '15 at 08:04
  • This sounds more like a design problem. If you're using PHP 5.4 or above you can try using traits to solve this issue. http://php.net/manual/en/language.oop5.traits.php – Derek Feb 07 '15 at 08:06
  • I appreciate your idea for Traits. But again Traits dependencies can't be change at run time , Over that i have to use Getter and Setter which again break the IoC principle as I wont be able to switch class at runtime – Sushant Feb 07 '15 at 08:33
  • I don't see any other way to do it other than defining the dependencies as parameters in the `__construct()` functions and cascading them (your case 1). In response to your last comment, you can use interfaces as the type for each dependency, that way any class that implements the interface will work. That won't solve your issue of too many child classes, but that all depends on what actual problem you're trying to solve and if it can be organized better - that information can't be gleaned from your initial post. – Derek Feb 07 '15 at 08:53
  • Thanks for insights. But I think I have to open a bounty to get more clear answers to ensure IoC for my case. Thanks again – Sushant Feb 07 '15 at 09:00
  • You could also use `func_get_args()` and `func_num_args()` in your child constructor and not define any specific parameters. Always send the first 2 to the `parent::__construct()` method and let that method deal with ensuring they're the proper type, then deal with remaining items however you want. – Derek Feb 07 '15 at 09:05
  • But for that I have to mention dependency in child constructor to get arguments. That will leave us to point where we started (multiple DI points) – Sushant Feb 07 '15 at 09:10
  • 1
    COMPOSITION OVER INHERITANCE :D – themightysapien Feb 25 '15 at 09:51
  • 1
    I agree with @Derek. Traits are a great way to deal with DI in php. They allow you to only inherit exactly what you need. They can accomplish all of the following or just what you need: Add new properties(services) to a class that previously did not exist, set those services to default types, provide setters so that you can change/override the type (allowing it to be changed at runtime), provide getters. – still_dreaming_1 Feb 25 '15 at 18:26
0

To keep things clean and maintainable for inheritance scenarios with a couple of dependencies, I personally favor setter injection over constructor injection. There are also some thoughts by Martin Fowler on this, which are worth a read.

You may also choose to inject the common dependencies with constructor injection and the child dependencies with setter injection to not mess up with the constructor.

Nevertheless, if you use setter injection it is very important to make sure an object is not partially initialized or missing some dependencies. A great way to ensure this is in the getter method of the service (albeit you'll only note this at runtime). Extending the example from Derek, you can define your getters as following:

trait ServiceATrait {
    private $A;

    public function initServiceA(ServiceA $serviceA) { 
        $this->A = $serviceA;
    }

    public function getServiceA() { 
        if (null === $this->A) {
            throw new \LogicException("ServiceA has not been initialized in object of type " . __CLASS__);
        }
        return $this->A; 
    }
}

Whatever option you choose, make sure to use it consistently across your code base.

Fabian Kleiser
  • 2,988
  • 3
  • 27
  • 44
  • 1
    i'd use `if (is_null($this->A)) {...` rather than `if (null === $this->A) {` but that's just personal preference – Derek Feb 26 '15 at 17:35
0

I'm not sure I understand what you are trying to accomplish by using inheritance. Inheritance should be used for things conceptually the same type, not for a common dependency injection point. If you have a natural case for using inheritance, and you want to use dependency injection as well, in order to follow good practices, then that makes sense. But if inheritance and constructor parameters are the way to go, you can't get around having to change them in lots of classes if you have lots of inheritance. But having lots of inheritance is usually a sign you are overusing inheritance. Inheritance is often overused. It makes the behavior of classes complex and difficult to read and change. Always look for an alternative to inheritance and only use it when it works better than the alternatives.

You have another code smell. If you need to pass enough constructor parameters that this is creating maintainability issues, then your classes are probably violating the single responsibility principle. This would mean you need to break up your classes into more classes. You may also need to change some of the constructor parameters to be method parameters for the appropriate methods.

As others have mentioned, you could use traits. That would make it easier to inherit only the exact behavior you want. The traits can both create properties, set them using some default type, and provide a way to both get and set those properties.

As far as your concerns about things being able to change at runtime, I am not sure what you mean. It is all running at runtime. It is all just variables in the end. Constructors can be called at runtime, and you can pass whatever values they need. You can use setters at runtime as well. Dependency injection is by definition a runtime technique. If your class is creating the types in question without providing a way to change the type via DI, then I guess it is not changeable at runtime, but nobody is suggesting that.

You could utilize factory classes or static factory methods. Static factory methods can have different names that describe how it will be constructed. Factory classes can have a single factory method, in which case you would set your factory instances to whichever factory will construct the objects properly.

Don't forget about default arguments. These should be kept in mind no matter how you go about dependency injection or constructing your objects. Example:

class someClass {
    public function _construct($serviceA = new serviceA()) {
        $this->serviceA = $serviceA;
    }
}
still_dreaming_1
  • 8,661
  • 6
  • 39
  • 56