Dependency Injection: a discussion of the pros and cons

This makes no sense anyway. You’re calling singleton::getInstance('address'); so you still have that exact problem, you have to look in singleton::getInstance to work out “exactly which class was instantiated into that particular object.”

The fact that “exactly which class was instantiated into that particular object.” is a concern of yours highlights that you don’t understand polymorphism. Which class was instantiated shouldn’t matter at all.

When you deliver the product as a black box(compiled/obfuscated),
when you do not need to prove the code stabillity from release to release,
when you do not care about object as a concept of a real world,
when your project contains plain and documented business logic,
and when you won’t let other people to contribute to your project
you are able not to use DI.

1 Like

You should read books and try some implementations written for Java, .Net.
I suggest you to read, at least “Dependency Injection in .NET: Mark Seemann” and then get back to the discussion.

I repeat, different groups of programmers have different ideas on what constitutes “best practice”, so stop telling me that there is a “one size fits all” document.

Just because some people have a problem with singletons does not mean that they are automatically bad for everyone else. If their use of singletons causes a problem, but my use does not, why should I care?

Just because DI does provide benefits in some circumstances does not automatically mean that it also provides benefits in all circumstances. I have already stated that I actually use DI in those circumstances where it provides benefits, so why should I also use it in those places where it does not?

Singletons do what they are supposed to do, which is to provide a single copy of an object where you would otherwise create multiple copies. Obtaining an existing instance from memory is supposed to be less costly than creating a brand new instance.

I disagree. Unlike other programmers I do not have a separate getInstance() method inside each and every class, I have a single copy of that method inside a separate singleton class. Your sample code which demonstrates DI is incomplete as it does not show how in TDD I can use a configuration file for switch that dependency from one object to another. As that is supposed to be the sole purpose of DI I find this to be a glaring omission.

I disagree. Singletons have a purpose, DI has a purpose, but the two are not the same. I require to use single instances of an object instead of generating multiple copies, so I elect to use singletons. I do NOT require to switch all my dependencies from one set of objects to another, so I elect NOT to use DI.

Again I disagree. Putting two lines of code in one place is much quicker than putting multiple lines of code in multiple places. Two lines of code will also be quicker to execute than more than two lines of code, not to mention easier and quicker to read and therefore easier to maintain.

Then how do you identify that you want this set of dependencies switched to that set of dependencies?

No. That’s a misunderstanding of DI. You do not need to use configuration files, you simply need to provide a mechanism to inject the class you want used into your consumer. How that object is determined (be it configuration or any other technique) is up to the developer.

However, we’ve yet to see your singleton class. So to claim the lack of a configuration file is a glaring omission, would also be to say the lack of the singleton class is also a glaring omission. Right?

There are many ways. Your test passes in a Mock Object for one. Configuration file is purely one of several different ways to accomplish it.

My singleton class is described in http://www.tonymarston.net/php-mysql/singleton.html

That’s not short and to the point! I want a minimal example that doesn’t rely on anything else or an understanding of anything but the code you present.

I think he is referring to this:

class singleton
// ensure that only a single instance exists for each class.
{
    function &getInstance ($class, $arg1=null)
    // implements the 'singleton' design pattern.
    {
        static $instances = array();  // array of instance names

        if (array_key_exists($class, $instances)) {
            // instance exists in array, so use it
            $instance =& $instances[$class];
            
        } else {
            // load the class file (if not already loaded)
            if (!class_exists($class)) {
                switch ($class) {
                    case 'date_class':
                        require_once 'std.datevalidation.class.inc';
                        break;

                    case 'encryption_class':
                        require_once 'std.encryption.class.inc';
                        break;

                    case 'validation_class':
                        require_once 'std.validation.class.inc';
                        break;

                    default:
                        require_once "classes/$class.class.inc";
                        break;
                } // switch
            } // if

            // instance does not exist, so create it
            $instances[$class] = new $class($arg1);
            $instance =& $instances[$class];
        } // if

        return $instance;

    } // getInstance
    
} // singleton

aha. So Tony,

how is this:


class Person {
    function doStuff () {
        ....
        $objAddress =& singleton::getInstance('address');
        $address = $objAddress->getPrimaryAddress($customer_id);
        ....
    } // doStuff
} // end class Person

class singleton
// ensure that only a single instance exists for each class.
{
    function &getInstance ($class, $arg1=null)
    // implements the 'singleton' design pattern.
    {
        static $instances = array();  // array of instance names

        if (array_key_exists($class, $instances)) {
            // instance exists in array, so use it
            $instance =& $instances[$class];
            
        } else {
            // load the class file (if not already loaded)
            if (!class_exists($class)) {
                switch ($class) {
                    case 'date_class':
                        require_once 'std.datevalidation.class.inc';
                        break;

                    case 'encryption_class':
                        require_once 'std.encryption.class.inc';
                        break;

                    case 'validation_class':
                        require_once 'std.validation.class.inc';
                        break;

                    default:
                        require_once "classes/$class.class.inc";
                        break;
                } // switch
            } // if

            // instance does not exist, so create it
            $instances[$class] = new $class($arg1);
            $instance =& $instances[$class];
        } // if

        return $instance;

    } // getInstance
    
} // singleton

 new Person();

Shorter and faster than:

class Person {
    public function __construct(Address $address) {
        $this->address = $address;
    }

    function doStuff () {
        ....
        $address = $this->address->getPrimaryAddress($customer_id);
        ....
    } // doStuff
} // end class Person

$address = new Address;
new Person($address);

I disagree. With limited exceptions the argument I use is always the name of a class, and that class name always refers to a table in the database, so I know exactly which class is being instantiated.

I disagree. Polymorphism allows the same method to be called on different objects with different results. This is entirely different from calling a method on a particular object in order to obtain a particular result. This means that I need to know the identity of the dependent object.

So know you are telling me that there are several possible ways in which DI can be implemented? This absolutely guarantees that should I ever decide to implement DI there will be someone somewhere who will immediately tell me that I have chosen the wrong implementation! It’s enough to make a saint swear! (and I ain’t no saint).

Why? Why do you care how the object builds its results? That is the point. Separation of Concerns. Your piece shouldn’t care, it should simply know, I have a type of X and I need to call Y on it. It object itself is supposed to care about how it processes that call, not your component.

That is because it is how you wrote your framework, that isn’t strictly true given a singleton. Take Tom and I, we don’t know that by looking at the code, we’d have to go look to see what that getInstance was doing when given “address” versus “date_validation” (which has special logic).

You missed his point, he meant there are multiple ways of implementing configuration for DI, but that’s irrelevant. The simplest is new A(new B(new C))) etc. DI is injecting dependencies, the mechanism you use to do that does not matter.

I disagree. Polymorphism allows the same method to be called on different objects with different results. This is entirely different from calling a method on a particular object in order to obtain a particular result. This means that I need to know the identity of the dependent object.

The point of polymorphism is is that you as someone using an object need only know what its API is. You don’t need to know what its class is. This is important because multiple classes can share the same API. This is the fundamental idea behind polymorphism.

For example, If I had a Rectangle, Circle and Triagle class, they can all have a getArea() method but I can write a method that can work with any shape:

function showArea($shape) {
 echo 'The area of your shape is ' . $shape->getArea();
}

This is the essence of Polymorphism in OOP. In OOP we strive to make methods that are agnostic about the implementation of collaborators. They know about the API but not the implementation. This allows methods to be usable with any type of shape, otherwise I need to create a method for each type of shape which leads to repeated code.

1 Like

This. This is exactly what I was trying to say :slight_smile:

With:

I’m afraid that this method of injecting dependencies just will not work in my framework because it changes the coupling between the Controller and the Model from very low to very high. My page controllers are pre-written and completely reusable by virtue of the fact that none of them contain any hard-coded names of the model classes which they instantiate. Each user transaction (unit of work) looks like the following:

<?php
$table_id = "person";                      // identify the Model
$screen   = 'person.detail.screen.inc';    // identify the View
require 'std.enquire1.inc';                // activate the Controller
?>

The file std.enquire1.inc (the page controller for the ENQUIRE1 pattern) contains code like the following:

require_once "classes/$table_id.class.inc";
$dbobject = new $table_id;
$result = $dbobject->doSomething();

As you can see the name of the model class is passed down to the page controller script from the transaction script. This means that I can reuse the same page controller script with any number of different values for $table_id, and in my current ERP application it is reused over 300 times.

By using YOUR proposed method of dependency injection it would immediately wipe out the reusability of my page controllers and instead make them tightly coupled to the model classes with which they communicate.

I’m afraid that the loss of such reusability and the introduction of such tight coupling would simply wipe out any benefits of the OO approach, and that would be totally unacceptable to me.

I’m afraid that it’s back to the drawing board for you. Nice try, but no cigar.

I’m just curious as to how end to end testing can easily be accomplished in a framework/application riddled with Singletons outside of relying on functional tests running a headless browser or Selenium. A major advantage or DI and DIC is ease of changing the internal state of an application within the same request cycle.

I’m really not sure what you’re talking about here.

class EnquireController {
    public function __construct($model)
    {
        $this->model = $model;
    }

    public function doStuff()
    {
        $result = $this->model->doSomething();
        // etc ...
    }
}
$controller = new EnquireController(new Person);
$controller->doStuff();

How is the above code any more tightly coupled than yours? The controller doesn’t know which model class will be passed in. Only the calling code knows which model and which controller classes are instantiated, which is no different from your example.

2 Likes