Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DI container, factory, or new for ephemeral objects?

When dealing with objects that require data known only at runtime, such as a username and password, where should object instantiation happen: by using new, in a factory, or in a DI container?

For example, I could just new an object once I have the data:

UserCredentials creds =
    new UserCredentials(dialog.getUsername(), dialog.getPassword());

Or, I could use a factory:

UserCredentials creds =
    CredentialsFactory.create(dialog.getUsername(), dialog.getPassword());

Or, I could use a provider within a DI container (which in this case would essentially be a parameter-driven factory). [Sample code omitted.]

It seems both wrong to use the DI container for something so simple yet it also seems wrong not to use it to its fullest.

like image 681
Kaleb Pederson Avatar asked Jan 13 '10 22:01

Kaleb Pederson


People also ask

What is Factory in dependency injection?

Dependency Injection is more of a architectural pattern for loosely coupling software components. Factory pattern is just one way to separate the responsibility of creating objects of other classes to another entity. Factory pattern can be called as a tool to implement DI.

How many dependencies should a class have?

The fact your class has so many dependencies indicates there are more than one responsibilities within the class. Often there is an implicit domain concept waiting to be made explicit by identifying it and making it into its own service. Generally speaking, most classes should never need more than 4-5 dependencies.

Why use Dependency Injection?

Dependency injection helps to develop testable code, allowing developers to write unit tests easily. You can use mock databases with dependency injection, and test your application without affecting the actual database.


2 Answers

As always, it depends, but as a general rule, a static factory like your second option is only rarely a good idea.

newing up a UserCredential object seems like a fair choice because the UserCredentials class looks like a self-contained, concrete class that can be fully instantiated with all its invariants from the username and password.

In other cases, the type you would like to create may represent an abstraction in itself. If this is the case, you can't use the new keyword, but must use an Abstract Factory instead.

Using an Abstract Factory is often very valuable because it allows you to compose an instance from a combination of run-time values and other dependencies. See here for more information.

Using an Abstract Factory also helps with unit testing because you can simply test that the return value or end state or whatever you care about is related to the output of the Abstract Factory - for which you can easily supply a Test Double because it is... abstract.

like image 144
Mark Seemann Avatar answered Oct 21 '22 09:10

Mark Seemann


The google testing blog has a post which tries to answer this question. The basic idea is that you can classify each of your classes as a "newable" or "injectable", and that it is OK to just "new up" the newables.

I distinguish 2 main categories of "newables":

  • values like int, string, DateTime, etcetera.
  • entities like Customer, Order, Employee, etcetera. I think your UserCredentials class falls under this heading.

It's important to realize that newables can have (testable) behavior. If you make the mistake of thinking that newables shouldn't have any behavior or unit tests, you'll end up with the anemic domain model anti-pattern.

A side-effect of having "newables" with behavior is that this behavior can't be abstracted away in the unit tests of your injectables. This is OK; it is normal to have some strong coupling between your domain model and the rest of your application.

Also, newables are allowed to know about injectables, but they only cooperate with them transiently. For example, UserCredentials should not take a IUserDatabase as a constructor argument. Instead, there might be a UserCredentials.Verify(IUserDatabase) method.

edit: I'm currently not so sure anymore about what I wrote above. Entities can also be constructed via (injectable) factories instead of calling their constructor directly. The factory implementation can then inject things in the entity.

like image 32
Wim Coenen Avatar answered Oct 21 '22 09:10

Wim Coenen