views:

89

answers:

3

Hi,

i'm looking for a larger example of dependency injection and how it can be implemented. If class A depends on class B and passes a reference of class C to B's constructor, must not class A also take a reference to class C in it's constructor? This means that the main method in the application should create all classes really, which sounds wierd?

I understand that using DI frameworks we can have it in XML files somehow, but that sounds like it could be hard to quickly see what type that really is instanciated? Especially if it a very large application.

+2  A: 

You are correct and each DI framework has a different way of managing it.

Some use attributes on the properties etc to denote dependency and then "automagically" supply an instance of the correct type, while others (say castle windsor for .net) allow xml configuration, fluent or other methods for "wiring up" the dependency graph.

Also no, class A takes a built reference to an instance of B which was built using an instance of C. A needs to know nothing about C unless exposed via B.

    public class C { }

    public class B { public B(C c) { ... }}

    public class A { public A(B b) { ... }}

    // manual wireup
    C c = new C();
    B b = new B(c);
    A a = new A(b);

   // DI framework
   InversionOfControlContainer container = new InversionOfControlContainer(... some configuration);
   A a = container.ResolveInstanceOf<A>();
   // container dynamically resolves the dependencies of A. 
   // it doesnt matter if the dependency chain on A is 100 classes long or 3.
   // you need an instance of A and it will give you one. 

Hope that helps.

Thomas James
But who does the manual wireup? Someone needs to do the "new".
Karl Trumstedt
You can do it in your code. If testing or using a simple example.
Thomas James
I use castle windsor and its xml format of configuration. It gives two xml attributes "service" and "type" iirc. This allows you to quickly scan the xml config to find the type implementing the given service.
Thomas James
I understand that you do it in your code, but the class that does the new becomes dependant on the other classes (and gets hard to test). Let's say we have 6 classes, A, B, C, D, E, F. F needs E, E needs D etc. Then class A needs to create all objects (or the main method which creates class A). Am I wrong?
Karl Trumstedt
Yes, you have to create the dependency somewhere. That is the purpose of the DI framework, that i is defined in configuration. That way you just ask your DI framework for an instance of "A" and it handles building the rest. Class A only needs to know about its dependencies. The code that creates an instance of class A needs to know how to get a reference to A's dependencies. When testing you should be mocking / faking A's dependencies so you can control the test.
Thomas James
I will try and edit my example to help
Thomas James
+1  A: 
  1. to answer your question about classes A,B,and C, A only needs a reference to B.
  2. Most DI frameworks do not require you to use XML for configuration. In fact, many people prefer not to use it. You can explicitly set things up in code, or use some kind of conventions or attributes for the container to infer what objects should fulfil dependencies.
  3. Most DI frameworks have a facility for "lazy loading" to avoid the creation of every single class up front. Or you could inject your own "factory or builder" objects to create things closer to the time when they will be used

You've not said what language you are using. My example below is in C# using the Unity container. (obviously normally you would use interfaces rather than concrete types):

container = new UnityContainer();
container.RegisterType<C>();
container.RegisterType<B>(); 
A a = container.Resolve<A>();
Mark Heath
+1  A: 

here's a few examples from the PHP Language, hope this helps you understand

class Users
{
    var $Database;
    public function __construct(Database $DB)
    {
        $this->Database = $DB;
    }
}
$Database = Database::getInstance();
$Users = new Users($Database);

From this example the new keyword is used in the method getInstance(), you can also do

$Users = new Users(Database::getInstance());

Or another way to tackle this is

class Users
{
    /*Dependencies*/
    private $database,$fileWriter;
    public function addDependency($Name,$Object)
    {
        $this->$Name = $Object;
        return $this;
    }
}
$Users = new Users();
$Users->addDependency('database',new Database)->addDependency('fileWriter',new FileWriter);

Update:

to be honest, I never use Dependency Injection as all its doing is passing objects into classes to create a local scope.

I tend to create a global entity, and store objects within that so there only ever stored in 1 variable.

Let me show you a small example:

abstract class Registry
{
    static $objects = array();
    public function get($name)
    {
        return isset(self::$objects[$name]) ? self::$objects[$name] : null;
    }

    public function set($name,$object)
    {
        self::$objects[$name] = $object;
    }
}

Ok the beauty of this type of class is

  • its very lightweight
  • it has a global scope
  • you can store anything such as resources

When your system loads up and your including and initializing all your objects you can just store them in here like so:

Registry::add('Database',new Database());
Registry::add('Reporter',new Reporter());

Where ever you are within your runtime you can just use this like a global variable:

class Users
{
    public function getUserById($id)
    {
         $query = "SELECT * FROM users WHERE user_id = :id";

         $resource = Registry::get("Database")->prepare($query);
         $resource->bindParam(':id',$id,PDO::PARAM_INT);

         if($resource->execute())
         {
              //etc
         }else
         {
              Registry::get('Reporter')->Add("Unable to select getUserById");
         }
    }
}

i see this way of object passing is much cleaner

RobertPitt
Yes, I understand this. But it's too small of an example I think. What about the class that uses Users? Should he create the database instance? Then that class becomes dependant and cannot be tested in a good way.
Karl Trumstedt
Please check my update, I think there's a better solution for you.
RobertPitt