2

I have an application where I'm using pure DI to construct my object graph. I would like to use a factory so I can create and destroy instances of a certain class at run time. The trouble is that this class has a lot (say >10) of dependencies on shared resources.

For example, to log in to a phone switch, you first need a socket connection to the switch itself, of which there can only be one per application. Then it allocates a number of call slots, each one to handle a session that can take phone calls one at a time. For each call slot, it allocates a phone extension from the extension pool, and all phone operations in the call slot must use the same extension. Then it needs to get an agent ID from the agent pool, and all operations in the call slot must use the same agent ID. Then it can finally login using the extension and agent ID. Finally it sends messages on some application level sockets to multiple other applications that want to know when login succeeds or fails.

Since I want these Login objects created on the fly, it seems like I should make a factory class in the Composition Root. It would take all these shared resources as constructor dependencies. But as the number of things that must happen at login grows, the factory class constructor parameter list is getting pretty big.

Here is some code. It is simpler than the actual application in that there are only 3 shared resources, but should convey the idea. To avoid long parameter lists in the create methods, I put the shared resources as class level variables and just access them directly in the create methods.

static SharedService1 service1;
static SharedService2 service2;
static SharedService3 service3;

static void Main(string[] args)
{
    service1 = new SharedService1();
    service2 = new SharedService2();
    service3 = new SharedService3();

    SessionOwner owner = new SessionOwner(
        CreateLoginSession()
        );
    //...
}

static LoginSession CreateLoginSession()
{
    return new LoginSession(
        CreateLoginExecutor(),
        CreateLogoutExecutor()
     );
}

static LoginExecutor CreateLoginExecutor()
{
    return new LoginExecutor(
                service1,
                service2
            );
}

static LogoutExecutor CreateLogoutExecutor()
{
    return new LogoutExecutor(
            new Foo(service2),
            new Bar(service3)
        );
}

Now I have determined that it needs to delay creation of the LoginSession object. A factory seemed like the right solution. The following does work:

class Program
{
    static SharedService1 service1;
    static SharedService2 service2;
    static SharedService3 service3;

    static void Main2(string[] args)
    {
        service1 = new SharedService1();
        service2 = new SharedService2();
        service3 = new SharedService3();

        DelayedSessionOwner owner = new DelayedSessionOwner(
            new LoginSessionFactory(service1, service2, service3)
            );
        //...
    }
}

class LoginSessionFactory
{
    private SharedService1 Service1;
    private SharedService2 Service2;
    private SharedService3 Service3;

    public LoginSessionFactory(SharedService1 service1, SharedService2 service2, SharedService3 service3)
    {
        Service1 = service1;
        Service2 = service2;
        Service3 = service3;
    }

    public LoginSession CreateLoginSession()
    {
        return new LoginSession(
            CreateLoginExecutor(),
            CreateLogoutExecutor()
            );
    }

    private LoginExecutor CreateLoginExecutor()
    {
        return new LoginExecutor(
                    Service1,
                    Service2
                );
    }

    private LogoutExecutor CreateLogoutExecutor()
    {
        return new LogoutExecutor(
                new Foo(Service2),
                new Bar(Service3)
            );
    }
}

But now all shared resources are parameters in the constructor of the factory. It might look ok here when there are only 3 shared services. But the real example has many more. Here are some solutions I thought of, none of which feel quite right.

  1. Use a DI container and pass it into the factory constructor in a single parameter. I know using a container vs. Pure DI has been debated (see here and here). I have done some work with a container on this application. The registration phase got really messy and hard for my coworkers to follow. This could be me not doing it right, but I had a hard time in a case like my phone example, where the application is a Windows service that can make multiple calls at once and so it had multiple instances of the same class.
  2. Put all the shared resources into a class, pass an instance of that to the factory, and let the factory use whichever resources it needs. Sort of like a really simple container, but specific to this application so the services would be accessible as basic properties. Then there could be a class that holds resources shared across the application, and another class, with a new instance for each call slot, that holds resources that are shared across calls on the same slot.
  3. Change the factory class to a factory method inside the main Composition Root class. Since there will be several of these factories, this could make the Composition Root class pretty big. I guess I could make it a partial class and split it up into multiple files.
  4. Just accept that the constructor list is going to be long because it really is using all the things in the list.

Any other ideas?

7
  • Are you really sure so many dependencies is really best design you can create? When I hear "lots of dependencies", my immediate reaction is "design smell".
    – Euphoric
    Sep 20, 2019 at 15:48
  • I would welcome another way to do it. The login task is composed is multiple steps - obtaining phone extensions, starting the microphone, sending socket messages. Some place in the code has to know enough about them all to put them into a single point of execution.
    – Rob L
    Sep 20, 2019 at 15:58
  • Maybe read cs.unibo.it/~cianca/wwwpages/ids/esempi/coffee.pdf to find out why creating abstraction around physical objects might not be best approach.
    – Euphoric
    Sep 20, 2019 at 16:23
  • @DocBrown This is not duplicate. This question is not asking about one or the other. It is asking about handling lots of dependencies when using DI factory. Rob is only using the non-DI approach as an example.
    – Euphoric
    Sep 20, 2019 at 16:25
  • 1
    The single responsibility of a factory is exactly to construct the objects you need with the dependencies they need. As such, it is conceptually a extension of the composition root, and it is totally appropriate to pass it a reference to the DI container so it can resolve all of the dependencies, just as the DI container itself would. To me, that sort of approach introduces less developer effort/risk/complexity than coming up with a bespoke solution to get around the "rule" that the DI container shouldn't be injected anywhere.
    – John Wu
    Sep 20, 2019 at 23:08

3 Answers 3

1

The single responsibility of a factory is exactly to construct the objects you need with the dependencies they need. As such, it is conceptually a extension of the composition root, and it is totally appropriate to pass it a reference to the DI container so it can resolve all of the dependencies, just as the DI container itself would. To me, that sort of approach introduces less developer effort/risk/complexity than coming up with a bespoke solution to get around the "rule" that the DI container shouldn't be injected anywhere.

That being said, I'd like to share a pattern with you that I use very commonly, which resolves the issue without needing to pass the container, and is also helpful in the case when you aren't using an IoC container at all, or wish to leave its control to someone else (e.g. if you are developing a library that has no composition root).

The idea is to set up the factory essentially to take the dependencies out of the constructor but expose them with builder methods that allow the caller (which might be the composition root) to quickly set the dependencies up.

In your example it would look like this:

class LoginSessionFactory
{
    private SharedService1 Service1 = null;
    private SharedService2 Service2 = null;
    private SharedService3 Service3 = null;

    public LoginSessionFactory()
    {
        //No constructor injection!
    }

    public LoginSessionFactory With(SharedService1 service1)
    {
        Service1 = service1;
        return this;
    }

    public LoginSessionFactory With(SharedService2 service2)
    {
        Service2 = service2;
        return this;
    }

    public LoginSessionFactory With(SharedService3 service3)
    {
        Service3 = service3;
        return this;
    }

    public LoginSessionFactory With(container)
    {
        service1 = container.Resolve<Service1>();
        service2 = container.Resolve<Service2>();
        service3 = container.Resolve<Service3>();
        return this;
    }

    public LoginSessionFactory With(Lazy<IContainer> container)
    {
        return With(container.Value);
    }

    //etc....
}

To instantiate the factory without an IoC container, you could use

var factory = new LoginSessionFactory()
    .With(service1)
    .With(service2)
    .With(service3);

And with the container you could use

var factory = new LoginSessionFactory()
    .With(myContainer);

Or in the composition root you might write it like this (depending on the capabilities of your container):

container.RegisterInstance<LoginSessionFactory>(new LoginSessionFactory().With( new Lazy<IContainer>( () => container )) );
2
  • resolves the issue without needing to pass the container This absolutely passes the container. You just passed it through a setter that returns this. Setter injection is still injecting the container which means the container had better be a parameter object or a service facade and deserves a more focused name than "container". It should not be a catch all container that resolves everything. Otherwise you're still hiding dependencies and violating SRP. Sep 21, 2019 at 11:38
  • @John Wu I totally agree with your first paragraph. I could solve by problem with a DI container, but my coworkers are reluctant. The rest of your example is an interesting idea. But even if I use just the parts without the container, what would it get me that I wouldn't get just by making the services public properties?
    – Rob L
    Sep 21, 2019 at 11:45
2

Having dealt with entry points where objects needed ~10 constructor parameters and abstract factories began growing to 4+ dependencies, I would advise erring more on the side of practicality than best practices, despite how "smelly" this may seem.

The best idea you will get "out of the box" is in the comment and answer of John Wu. You should create a mini DI container with the services you would like to distribute and pass it to a factory that undertakes the task of creating and passing them around. This is safe because you have full and focused control of what is "magically" going on in one place (the factory). You can intercept (and, as a result, log, or perform additional cross-cutting concerns) requests for specific services easily because you will be "passing them around" through specific methods (i.e. Create***) and not uncontrollably through blind requests (cf. Service Locator).

However, the "smell" you also need to be aware of is that you will be creating a Facade Service, though one that contains it all together, i.e. the abstract factory. The true problem with your abstract factory is that it is going to become a service locator in the end, only it will (appear to) also undertake the creation of the passed dependencies but, who cares anyway? The classes using the factory to construct things will be hiding their dependencies behind the factory. The question you have to ask yourself is: Are you prepared to live with that?

Now, I am not going to argue that a constructor with dozens of parameters is good. However, I am going to argue that, at times, a constructor with, say, 6 parameters is better than a constructor with one parameter of an abstract factory. Many programmers (citation needed, I know, that's why I stick to "many" and not "most") reading your API would not appreciate having to visit the source code of the class to see which of the components that the factory can create are actually created by the class. When I see something like this, based on my past experience (we all hold the grudge of past experience, unfortunately), I will judge based on my own sense of good practices. I will assume that everything the factory can create is eventually needed inside this class. Will I be wrong? How much time do I have to spend reading the source code, to find out? You will feel better with your classes having 1-2 parameters, and I will feel perplexed from having to walk-through your classes to know what they actually depend on.

My tiresome analysis is just to make the point that 10 constructor parameters in 1 class located at the absolute composition root of an application with more than, like, 100+ other well-designed classes (if not many more, of course) is not the same as having an application with a program with the same situation for a class located well below the root (i.e. down the hierarchy), in an application with far less classes in total. There is an "abstract" organizational quality "score" for your overall design and if it is built in a really solid, sensible and maintainable manner, you don't "lose" significant readability points from a top-level "orchestrator" class having a lot of parameters. Plus... if you need to deliver the application, like, tomorrow, 10 parameters in a working class built solely in (or very close, "vertically", to) the composition root is a great place to not touch at all.

Now that we got this all out of the way I have an additional suggestion, based on my (mis)understanding of your specific problem:

You say you have many unrelated dependencies and you need them all inside one abstract factory. This gives me the idea that they will not always be simultaneously necessary everywhere. I get the feeling you could simply break down your abstract factory to... more-than-one abstract factories and pass them around in subsets. How many "more-than-one"? Well, I would suggest enough of them, in order to make sure that when an abstract factory is injected in a specific class, the class will use all Create*** methods of the factory, so you will adhere to the simple wonderful convention that the factory does not obscure the dependencies of the class while successfully abstracting away actual dependency creation. This way, you get your benefits without increasing the inherent doubt of future readers of your API.

Well, if I am not misunderstanding anything, that is...

Update (based on comment)

You say a Login session depends on 10 actions being composed into one object. There is probably a reason behind each and every one of these 10 components. 10 components means there are 10 things that can be done in a different manner each, to get a Login session. Different combinations of 10 differently instantiated components will give you a different Login session. If so, then your strength lies behind the fact that you can inject a different "creator" for each of these session components and your application will adapt to work with the new configuration.

If you create an abstract factory tasked with constructing all 10 of these components, you are taking this strength away. You can now inject different implementations of this one factory to perform one set of re-adaptations each time, maybe changing the construction method of 1 component, 2, 3 or even all of them, at once. So with each different injection you get a full set of 10 specific construction methods and if you want to re-combine component construction methods you have to create a new factory that uses the new methods each time.

You may say that this might make some sense because a subset of the components tends to be configured, in a way, "at the same time", i.e. it does not make sense to create a different implementation of the factory to change the construction method for a single component, but it would make sense to create a new factory implementation that builds 2-3 or more specific components in a different specific manner. If that is the case, then you have a Façade, these components make sense together so they should be reconfigured together. Use a new factory for these.

In any case, when I am talking about splitting up the factories I don't mean making a factory for each component and passing these separate factories instead. I am saying that you can create, for example, 3 abstract factories tasked with constructing the 3, the other 3 and the rest 4 of the 10 components each. Then you can create the Login session factory that takes these 3 factories in the constructor and uses them to construct everything. Now you have no more than 4 parameters in each factory. The separate constructor parameter sets (3, 3 and 4 services each) of each factory might not make total sense in isolation but the 3 factories together make perfect sense, they are what is necessary to build Login sessions.

5
  • "Many programmers ... reading your API would not appreciate having to visit the source code". Isn't that the point of putting the factory in the composition root? Knowing that it will never be reused, so it isn't part of an API that other programmers would be calling into?
    – Rob L
    Sep 21, 2019 at 11:49
  • To answer the questions of your last paragraph, in the actual application, there are several factories in the real application. Every factory might not use every shared service, but for example, there will be factory that creates the login session when the user clicks a button. Let's say kicking off the login session requires a composite action made up of 10 other smaller actions. Then the factory that creates the session would create 10 actions and compose them together. Are you saying that each of the 10 actions should have its own factory? That seems like a lot of factories.
    – Rob L
    Sep 21, 2019 at 11:58
  • @RobL "Isn't that the point of putting the factory in the composition root? Knowing that it will never be reused, so it isn't part of an API that other programmers would be calling into?" - Yes, I am sorry, I was referring to your coworkers (especially newcomers) who will be co-maintaining the code base. In your case, of course you may not even have an API, besides, you are building an application, not a library, if I got that right. Sep 21, 2019 at 14:42
  • "if you want to re-combine component construction methods you have to create a new factory that uses the new methods each time" I guess I see the factory as part of the composition root. If I change the steps of the login process, I have to change it somewhere in the composition root regardless. It doesn't seem any different to me whether I change it in this factory or in any other composition root class.
    – Rob L
    Sep 21, 2019 at 15:32
  • 1
    Yes, so you probably only need it for lazy instantiation, plus at-will instantiation from within your other classes, during run-time. If this is only part of the composition root, having some more parameters in the construction might not hurt that much if it keeps things clear as to what depends on what. You have to pass these dependencies somehow to the factory anyway. Sep 21, 2019 at 18:24
1

If you wanna use Pure DI we can do that. It means we're not using a container.

If you wanna use code like Abstract Factory we can do that. But we're not doing it because we need parallel hierarchies. Nah, we just wanna hang a factory method off an object and get the polymorphic goodness that static factory methods don't give ya.

We can use the composition root to compose our code. This is just a fancy word for main() unless you're trapped in some framework that stole main() from you. In that case it means get as high up the call stack as you can get and ensure you're only called once. This is where we build our long lived object graph.

Once there the brain dead simple Pure DI pattern is to just build every long lived object before you need to pass it into some other long lived object you're building. Once done call one start method on one object and the whole thing starts ticking in all of it's object oriented glory.

That's right, the typical composition root is a straightforward pile of procedural code. It's not object oriented at all. It's what your OOP crawls out of.

But you want to break this up with a factory. Well that's fine. Actually it's more than fine. It's often required. Remember that all we're building here are the long lived objects that don't die until main exits. Plenty other objects need to be built later. You can't even build timestamps here.

Many of these, oh let's be fancy and call them, ephemeral objects have dependencies. They deserve some love and attention as well. Most importantly they deserve a place to resolve these dependencies.

These dependencies have their own lifetime. The essential question then, are these dependencies long lived or ephemeral?

If you want to build an ephemeral object that has long lived dependencies the answer here is simple: Build a long lived Abstract Factory Object (AFO) that holds those dependencies and hang that abstract factory method off of it. Pass this AFO to anything that needs your ephemeral object built on demand.

What if the ephemeral object also has ephemeral dependencies? Well the factory method hanging off your AFO can accept them as parameters. Now you have a place to do your ephemeral construction that is separated from your behavioral code.

This lets you separate two kinds of construction in time. It also lets you keep construction separate from the rest of your OO code. What it doesn't do is deal with those long parameter lists you were complaining about.

You're in luck. You're using C#. You have named arguments. If you're anything like me anything more that 3 arguments starts to make it hard to remember which position is what. Named arguments take care of that by letting you write code like this:

SwitchConnection theOneAndOnlySwitchConnection = new SwitchConnection();

LoginSessionFactory lsf = new LoginSessionFactory(
    SwitchConnection: theOneAndOnlySwitchConnection,
    CallSlotAllocator: new CallSlotAllocator(),
    ExtentionPool: new ExtentionPool(),
    AgentPool: new AgentPool()
    ...
);

Or since you also have Optional Arguments it could simply be:

LoginSessionFactory lsf = new LoginSessionFactory(
    SwitchConnection: theOneAndOnlySwitchConnection
);

Because unless you're absolutely opposed to hardcoding obvious defaults, making them overridable defaults is very powerful and still enables testing. What did it really cost you? The name. If you ever want a factory with different hardcoded overridable defaults you're going to have to think of a new name for it. Boo hoo.

Yeah that's right, there might be more than one factory someday. So give the poor thing an interface and we'll just keep which implementation of it we're using now a secret from the rest of the code.

Pass lsf into whatever object will use it when it's time to use it and let it pass in the loginListeners that it now knows care about this and bang you have your LoginSession object with all the state info it needs to attempt logging in.

loginSession = lsf.build(loginListeners);
loginSession.login();

loginSession can now tell the ephemeral listeners all about what happened.

When you create a behavior object like LoginSession do not hide dependencies. Behavior classes should expose all their dependencies as injectable.

When you create construction code hide dependencies. No really. This keeps main() from becoming a huge mess when that procedural code gets big. But it'll costs you names. Your ability to come up with descriptive names for these factories-that-hide-dependencies is the serious limiting factor here. Those names limit how useful people will find them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.