6

My Dispatcher is "choosing" correct Controller; then creating Controller's instance (DependencyInjectionContainer is passed to Controller constructor); then calling some Controller's method...

class UserController extends Controller
{

  public function __construct(DependencyInjectionContainer $injection) {
    $this->container = $injection;
  }

  public function detailsAction() {
    ...
  }

}

DependencyInjectionContainer contains DB adapter object, Config object etc. Now let's see what detailsAction() method contains...

public function detailsAction() {

  $model = new UserModel();
  $model->getDetails(12345);

}

As you see I'm creating new instance of UserModel and calling getDetails methods. Model's getDetails() method should connect to db to get information about user. To connect to DB UserModel should be able to access DB adapter.

What is the right way to pass DependencyInjectionContainer to the UserModel? I think that this way is wrong...

public function detailsAction() {

  $model = new UserModel($this->container);
  $model->getDetails(12345);

}
Kirzilla
  • 16,368
  • 26
  • 84
  • 129
  • This is essentiall a duplicate of this: http://stackoverflow.com/questions/2386487/is-it-better-to-create-a-singleton-to-access-unity-container-or-pass-it-through-t – Mark Seemann Mar 09 '10 at 12:37
  • I've just read this http://stackoverflow.com/questions/2386487/is-it-better-to-create-a-singleton-to-access-unity-container-or-pass-it-through-t and this http://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2/1475861#1475861 but I'm still confused. So, should I pass DependencyInjectionObject to any new instance by chain (from one to another)? – Kirzilla Mar 09 '10 at 12:47
  • @Mark Seemann Wow, you've wrote entire book about Dependency Injection! Damn, DI seems to me more and more difficult with every question :) – Kirzilla Mar 09 '10 at 12:53
  • 1
    @Kirzilla: It's really not *that* difficult, but the devil is in the details :) If you don't mind an answer with C# code, I'll make an attempt to answer - just say the word :) – Mark Seemann Mar 09 '10 at 12:56
  • Of course! I don't think that C# syntax is to difficult to read and understand. – Kirzilla Mar 09 '10 at 12:58
  • ;) I mean of course, you're welcome! :) – Kirzilla Mar 09 '10 at 17:03

2 Answers2

10

Instead of injecting the entire DI Container into your classes, you should inject only the dependencies you need.

Your UserController requires a DB Adapter (let's call this interface IDBAdapter). In C# this might look like this:

public class UserController
{
    private readonly IDBAdapter db;

    public UserController(IDBAdapter db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public void DetailsAction()
    {
        var model = new UserModel(this.db);
        model.GetDetails(12345);
    }
}

In this case we are injectiing the dependency into the UserModel. In most cases, however, I would tend to consider it a DI smell if the UserController only takes a dependency to pass it on, so a better approach might be for the UserController to take a dependency on an Abstract Factory like this one:

public interface IUserModelFactory
{
    UserModel Create();
}

In this variation, the UserController might look like this:

public class UserController
{
    private readonly IUserModelFactory factory;

    public UserController(IUserModelFactory factory)
    {
        if (factory == null)
        {
            throw new ArgumentNullException("factory");
        }

        this.factory = factory;
    }

    public void DetailsAction()
    {
        var model = this.factory.Create();
        model.GetDetails(12345);
    }
}

and you could define a concrete UserModelFactory that takes a dependency on IDBAdapter:

public class UserModelFactory : IUserModelFactory
{
    private readonly IDBAdapter db;

    public UserModelFactory(IDBAdapter db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public UserModel Create()
    {
        return new UserModel(this.db);
    }
}

This gives you better separation of concerns.

If you need more than one dependency, you just inject them through the constructor. When you start to get too many, it's a sign that you are violating the Single Responsibility Principle, and it's time to refactor to Aggregate Services.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • @Mark Seemann, thank your for your anwer! I will read it some times to understand the advantages. – Kirzilla Mar 10 '10 at 09:57
  • I see the only disadvantage of your method (imho). What if I'm calling UserModel method which don't need any connection to DB? All Dependency Injection Objects passed to UserModel constructor are already being created in UserModelFactory. (This all is about performance). What do you think of this realization of DIContainer? http://twittee.org/ – Kirzilla Mar 10 '10 at 10:08
  • If the UserModel doesn't use the DB in all cases you might want to consider whether it should take a dependency on it at all. In any case, you don't solve any issues by injecting the container instead of specific dependencies, and I generally don't buy the argument about performance, because expensive services (like the DB adapter) tend to be shared amongst many consumers, and even if this isn't the case, there are ways to deal with that: http://blog.ploeh.dk/2010/01/20/RebuttalConstructorOverinjectionAntipattern.aspx – Mark Seemann Mar 10 '10 at 10:23
  • By the way, using your method I can encapsulate any DIContainer Objects. It is impossible to do if I will inject whole DIContainer. Thank you! – Kirzilla Mar 10 '10 at 14:07
  • @Mark, a bit outside the subject but still on the same topic: how about a Domain Model? Would you make a factory for it or is it ok to instantiate it inside a controller especialy if it depends on run time parameters? Example: $objDictionary = new Dictionary($_GET['language']); – johnlemon Sep 16 '13 at 17:51
  • If you follow e.g. *ports and adapters* architecture, you'll need some sort of translation/mapping/anti-corruption layer between your run-time and the Domain Model. Such a thing will often contain classes indistinguishable from Abstract Factories. – Mark Seemann Sep 16 '13 at 20:39
-1

I'd use a singleton object for all config parameters : You set it up in your bootstrap, then choose to use it directly or pass it as parameter in your objects.

The idea being to have one method all around to retrieve your config data.

You may then provide an abstract class for db manipulation which uses your config. singleton. DependancyInjection can still be used to override your default data.

The above link in the comment (possible 'duplicate') concludes on using constructor injection : this is close to your current method.

However if I try to figure how your model works, I guess you will have many other model classes other than "userModel". Thus an abstract class using a config singleton might be a good solution : all your next model classes will just extend this abstract class, and you don't have to worry about your config.

On the other hand, your solution is good to me as long as your dependanceInjectionContainer changes often.

Rodolphe
  • 507
  • 2
  • 7
  • Frankly speaking, objects in my dependanceInjectionContainer are not changing often. I'm storing DB Adapter, Memcache Adapter, Mail Adapter and Config in dependanceInjectionContainer. So, probably, some kind of Registry is ok for me. I can't even imagine situation when I should change anything in dependanceInjectionContainer (any real life situation samles?) – Kirzilla Mar 09 '10 at 13:07
  • As far as my humble experience can speak, I have not yet seen any use of DI for a controller. Maybe if you need to juggle with multiple databases ? or if you pass some user specific parameters (providing a theme for exemple) – Rodolphe Mar 09 '10 at 13:17
  • By the way, I can make dependanceInjectionContainer a Singleton and get it's instance in Controller or Model. It will help me to avoid passing dependanceInjectionContainer into constructor. So, let's wait for Mr. Mark Seemann answer to see what is the key.... – Kirzilla Mar 09 '10 at 13:20
  • Yeah, I'm dealing with multiple databases and memcaches, but I don't think that it changes anything. I can keep DB_1 Adapter & DB_2 Adapter in dependanceInjectionContainer as well as I can keep it in Registry singleton. – Kirzilla Mar 09 '10 at 13:22
  • Just found pretty nice article about Dependency Injection in ZendFramework http://www.developertutorials.com/tutorials/php/the-zend-framework-dependency-injection-and-zend-di-8-02-07/page1.html – Kirzilla Mar 09 '10 at 13:27