2

I've been watching Google's clean code talks by Misko Hevery. These talks say: ask for dependencies in the constructor, so other programmers can see exactly what is needed up front, to instantiate an instance of a given object (law of demeter). This also makes testing easier as a programmer knows exactly what needs to be mocked.

Example time
If I have a class Customer and I also have a CustomerDAO class to abstract data access away. When I construct a customer object I might do the following:

database = new Database('dsn');
customerDao = new CustomerDAO(database);
customer = new Customer(customerDao);

This might happen in my controller. I can simplify this object construction via use of a dependency injection container. Below I've used a DI container to obtain an instance of my database class, as that is widely used throughout my application. This reduces the construction code to one place and can be mocked for testing.

Should I be adding my domain class dependencies (in this case DAO objects) to my DI container? If my application is large, will this make my DI container huge?

Using a DI container my code might look like this:

// container setup
container->dsn = '...';
container->dbh = function($c) {
    return new Database($c->dsn);
};
container->customerDao = function($c) {
    return new CustomerDAO($c->dbh);
};

// controller code
class ControllerCustomer extends ControllerBase {

    public function index() {
        container = this->getContainer();
        customer = new Customer(container->customerDao);
        view->customerName = customer->getName();
        view->render();
    }

}

Seems to be OK, if another programmer wants to test Customer, they need only mock CustomerDAO.

Taking this example a step further, if I have domain classes with dependencies on other domain classes, surely my DI container should not need to know how to construct every domain class? For example:

My customer might be a company/institution and therefore have many users.

class Customer {

  protected _dao;

  public function Customer(dao) {
    _dao = dao;
  }

  public function listUsers() {
    userIds = _dao->getAllUserIds();
    users = array();
    foreach (userIds as uid) {
      user = new User(new UserDAO(new Database('dsn')); // problem
      users[] user->load(uid);
    }
    return users;
  }

}

Problems

  1. As I've not passed my DI container to my Customer object, it can't create user objects as shown above as it has no reference to the database DSN (and shouldn't really need to know how to make users)
  2. Creating it's own dependencies makes this code untestable as they're concrete with no seams for mocking.
  3. If I do pass the container to my Customer class, does this make my interface for Customer lie? (See 9:15 in the linked Google video).

Should I be passing a user factory to Customer to enable it to construct User objects?

database = new Database('dsn');
userDao = new UserDAO(database);
userFactory = new UserFactory(userDao);
customer = new Customer(customerDao, userFactory);

Should the construction for UserFactory be in my DI container?

Greg K
  • 10,770
  • 10
  • 45
  • 62
  • Possible duplicate: http://stackoverflow.com/questions/4835046/why-not-use-an-ioc-container-to-resolve-dependencies-for-entities-business-object – Mark Seemann May 29 '11 at 09:50
  • Related: http://stackoverflow.com/questions/1891778/injecting-the-dependency-injector-using-dependency-injection – Mark Seemann May 29 '11 at 09:51

1 Answers1

0

If I am interpreting this correctly, it seems like your question is actually about entity construction and lifecycle management.

DDD is one design approach which provides very prescriptive guidance on how to approach problems like these; in your case the relevant concepts are repositories and aggregate roots. While DDD probably won't answer your question directly, it will make it much easier for you to come up with a pattern-based solution which matches your requirements.

I'm purposely not attempting to explain DDD in general or the concepts I mentioned; there is enough material about that available on SO and elsewhere.

Addys
  • 2,461
  • 15
  • 24