Dependency Injection leads to proliferation of factories?
Asked Answered
C

2

7

I have always felt uncomfortable when dealing with classes that needed to instantiate a lot of objects since I've been using Dependency Injection principles.

For instance, let's say I have a class that's supposed to raise a lot of different kind of events. Each event has a different type, so what I'd do is to have a different factory for each different event type. If I have 10 events, then I'll have to have 10 factories. That doesn't seem nice. I could also have a single factory for all the different kinds of events, but that doesn't seem too right, also.

(for the C# crowd, I'm not talking about .NET's events here. This was just an example to get to the point, just think of them as regular classes!)

This was just an example. I don't have a problem in having a factory here or there, but in certain kinds of projects where one has to create lot of objects at run-time, it seems as I have to make a factory for almost every class I define!

How do you handle this situation? Am I missing something?

I've seen people just passing around a reference to the IoC Container they use, but that doesn't seem any good to me. IMO, the domain model shouldn't even know a IoC Container is being used!

Thanks

Ced answered 24/5, 2011 at 11:59 Comment(0)
A
2

There's nothing wrong with a class that instantiates many other objects. Instead, that class should be treated as an aggregate root domain entity. As for different "types" of entity, if you assume they implement the same interface or inherit from the same base class, then passing a type argument to Factory.create(type) is how I usually go about approaching this issue. The internals of create() may delegate to other classes ala the Strategy pattern, but the client facing API is straightforward.

Now if you're creating a factory for every class you're dealing with, that sounds like something of anti-pattern. Study the aggregate root pattern mentioned above and see if you can organize your entities into graphs - you should be able to - such that one factory suffices for generating the whole graph.

As for IoC, the domain model should not know about the container. When I have entities that need references to singletons - usually factories - in the container I usually inject one factory into another as such:

class FactoryA {
    void setFactoryB(FactoryB factoryB) { /* sets into state */ }
    EntityA create(Enum type) {
        EntityA entityA = new EntityA();
        /* DI FactoryB - this method is probably default access */
        entityA.setFactoryB(getFactoryB());
    }
}

class FactoryB {}

So in the above example both FactoryA and FactoryB are singletons managed by the IoC container. EntityA needs a reference to FactoryB, so FactoryA is injected with a reference to FactoryB that's passed along inside the create() method.

Astilbe answered 24/5, 2011 at 12:36 Comment(0)
M
6

I've been a fan of constructor injection for as long as I've known about DI, because to my mind that's what a constructor is for. It states "I need instances of the following classes/interfaces in order to do my job" - e.g. pass me a File and an PrintWriter and I'll write the contents of the former to the latter.

And of course, this means that the code isn't aware of the DI framework, since the class is just being constructed with its required dependencies passed in. Additionally, I don't see the need for factories in this scenario, since the class is its own factory via the constructor.


As for the scenario you've identified in the second paragraph, I'm not sure if that's strictly related to dependency injection, but simply the design of the class hierarchy and responsibilities. Either your class knows specifically how to create an Event that corresponds to e.g. a failed login, in which case it does so (i.e. by calling the class' constructor); or, it doesn't know how to, in which case it will have to delegate to some kind of factory to create the Event.

And in this case you could set up your class to use the same factory to create all ten events (define an EventFactory interface with 10 methods), or you could define a separate factory interface (& implementation) for each type of event that needs contructing. In this latter case you'd also need to pass in ten different factories in your main class' constructor.

But again - this isn't anything to do with DI IMHO, it's a question of how you design your classes for flexibility (or "enterprise-ness") vs. straightforwardness. The two are orthogonal, in my thinking; you first define what collaborators your class needs (ten factories, one factory, or zero factories) and then you use DI to provide those dependencies. The presence or otherwise of DI shouldn't impact on your class design.

Matney answered 24/5, 2011 at 12:15 Comment(1)
To be honest, wouldn't it be for the need to mock the instances that are being created, I'd just plain 'new them without using a factory :(.Ced
A
2

There's nothing wrong with a class that instantiates many other objects. Instead, that class should be treated as an aggregate root domain entity. As for different "types" of entity, if you assume they implement the same interface or inherit from the same base class, then passing a type argument to Factory.create(type) is how I usually go about approaching this issue. The internals of create() may delegate to other classes ala the Strategy pattern, but the client facing API is straightforward.

Now if you're creating a factory for every class you're dealing with, that sounds like something of anti-pattern. Study the aggregate root pattern mentioned above and see if you can organize your entities into graphs - you should be able to - such that one factory suffices for generating the whole graph.

As for IoC, the domain model should not know about the container. When I have entities that need references to singletons - usually factories - in the container I usually inject one factory into another as such:

class FactoryA {
    void setFactoryB(FactoryB factoryB) { /* sets into state */ }
    EntityA create(Enum type) {
        EntityA entityA = new EntityA();
        /* DI FactoryB - this method is probably default access */
        entityA.setFactoryB(getFactoryB());
    }
}

class FactoryB {}

So in the above example both FactoryA and FactoryB are singletons managed by the IoC container. EntityA needs a reference to FactoryB, so FactoryA is injected with a reference to FactoryB that's passed along inside the create() method.

Astilbe answered 24/5, 2011 at 12:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.