0

I have a System which depends on Transactions, and a few other things. This list can become very long. Transactions can be (and will be) implemented in two ways or more, so every transaction is an interface.

public class System
{
    ctor(ITransactionA a, ITransactionB b, ... ITransactionN n, ...)
}

In my DI tool, I register which implementation of ITransaction I want to use.

I want to simplify the constructor.

One option is to use factories. But then I have a ton of extra code and I need to supply some kind of way to choose which ITransaction to use. I don't have the time or the man power to maintain the extra code. The right choice is to register the ITransactions into the DI tool anyway - the root is the right place to make this decision. The System should not be choosing which implementation to use, and if I used abstract factories I would end up with as many as there are ways to define the n transactions.

In this case, is it bad practice (that is, will this technique cause any engineering problems) to create a mini-container for the sake of constructor simplification?

public class ConcreteTransactionsContainer
{
    public ITransactionA {get;}
    public ITransactionB {get;}
    ...
    public ITransactionN {get;}

    ctor(ITransactionA a, ITransactionB b, ... ITransactionN n)
}

public class System
{
    ctor(ConcreteTransactionContainer trxs, IOtherThing other, IAnother, another)
}

This way, I control which implementation to provide in the DI tool, but the important code, where anyone who has to maintain code will be, is simple and easy to follow, without the overhead of a complex set of factories.

Or am I agonizing too much over the long constructor? I really hate long constructor signatures.

  • There are other ways to simplify constructor arguments, like passing a small DTO. But people are so enamored with DI nowadays that they'll probably reach for that automatically. That said, I've seen folks write DI containers using a dozen lines of code, so I know it can be done. – Robert Harvey May 12 '16 at 15:41
  • Are ITransactionA, ITransactionB, ... sharing some behaviour that is used in System ? – Spotted May 12 '16 at 19:27
1

In this case, is it bad practice (that is, will this technique cause any engineering problems)

On a technical level, no problem. You're basically adding a layer inside the dependency graph in order to reduce how much branching is going on. It shouldn't cause any technical problems. (If it actually matters for instantiation-speed, something has already gone very wrong.)

The real challenge involves concepts and communication: If possible, you want to come up with a name for your ConcreteTransactionsContainer that really makes you feel like you've captured an important concept in your overall system, something which was previously just implicit.

Is there something that defines/describes that design-time collection of Transaction objects beyond "my System happens to need them"? Are there ever going to be "siblings" to System that require a different subset of the transaction-collection?

  • I don't understand the peril here. Are you suggesting that every object in your system correspond to some tangible concept in the business domain? That's a laudable goal, but I doubt that it ever works out that way in practice. – Robert Harvey May 12 '16 at 16:24
  • Not necessarily the business domain (although that'd be nice) but at least something with a bit more rigor ExactlyTheFoosNeededByMyBar. – Darien May 12 '16 at 17:47
  • Oh, I see. Well, there are ways around that, but they amount to syntactic sugar for passing multiple constructor parameters anyway. At the end of the day, that's all DI really is, though I concede that you get a bit more value with a DI container. – Robert Harvey May 12 '16 at 17:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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