John Smith
John Smith

Reputation: 6207

Php, Dependency Injection, how to replace objects (for testing)?

we all know the simple DI: reduces coupling, and enable to replace objects blahblahblah:

class testA
{
    public function m() {}
}

class testB
{
    public function m() {}
}

class Prj
{
    /**
     * @var testA
     */
    private $obj;

    /**
     *
     */
    public function __construct(testA $obj)
    {
         // $this->obj = new testA();
         $this->obj = $obj;
         $this->obj->m();
    }
}

$obj = new testA();
$project = new Prj($obj);

I understand that $this->obj = new testA(); introduces tight coupling, if I had been done inside Prj::constructor. Thats right - and "__construct(testA $obj)" will not? This also hints that "testA" is needed. And how to even replace it? This:

$obj = new testB();
$project = new Prj($obj); // FAIL!

just wont run, since it expects "testA" not "testB". Another, hacky way:

class testB extends testA
{
    public function m() {}
}

works until "testA" is not final or testB needs to inherit from other class... so right now that "tight coupling" doesnt seem to be eliminated for me. And if someone says "simply remove testA hinting": what if I was developing with Java or other strong-typed language?

Upvotes: 1

Views: 96

Answers (1)

deceze
deceze

Reputation: 522501

Thats right - and "__construct(testA $obj)" will not?

No, testA as a type hint allows more wiggle room: it allows you to pass an instance of testA or any subclass of it. As you have shown, you can pass testB as long as testB extends testA. That means you have a means of altering the behaviour.

If you want it even more flexibly (as you have correctly pointed out, while more flexible than new testA inside __construct, it still has some limitations), you can use an interface:

interface CanM {
    public function m();
}

class testA implements CanM {
    public function m() {}
}

...

    public function __construct(CanM $obj)

Now no particular class hierarchy is being imposed on you, all you need to do is pass an object that implements CanM, which means that it needs to have a method m conforming to the specified function signature.

Upvotes: 3

Related Questions