Search code examples
c#dependency-injectionunity-containerfactoryfactory-pattern

Factory Object or Function, or Pass Unity Container?


I have an object that provides a sequence value to various consumers.

The provider must manage many sequences, because the consumers could ask to get a new value for any one of the ongoing sequences.

All of the sequences follow the same pattern, but they are path dependent and thus carry a lot of state. So the work of generating the sequence is delegated off to another object.

The the consumers might say, "Please give me the next value in the sequence named 'George'" and the provider will see if a sequence named George is already in progress, and if not it wants to start a new one (which it will then manage).

Sample:

class myValueProvider
{
    private Dictionary<string, IPathDependentValueGenerator > NamedSequences = new Dictionary<string, IMyInterface>();

    public IMyInterface GetNextValueForName(string SequenceName)
    {
        IPathDependentValueGenerator O;
        NamedSequences.TryGetValue(SequenceName, out O);
        if (o == null)
        {
            O = ????? /// How do I make my new value generator?
            NamedSequences.Add(key, O);
        }

    }

...so above, where I have the ?????? is the problem I'm trying to solve.

If you want to try and map this into a real world problem, the best easy example I can think of would be serial numbers.

If this were a serial number provider, then it would be able to generate the "Next" serial number for many different product lines. So if someone asks it for the next serial number for a "Green Shoe Model #3" it would provide the next value in that series. Then when asked for the next serial number for a "Yellow Bicycle" it will provide the next value in that series.

The provider doesn't need to know the details of the calculation used to get the next value, and in fact that calculation has to be swap-able. Hence my desire to inject it in some way.

When the provider needs a new O, that O must be correctly configured - that requires knowledge that the provider doesn't have (but is known at the time the DI container is created).

Furthermore, at least at the moment, when the Provider needs a new O, the O it wants will always be the same (so all O's will get created with the same type, constructor arguments, and injected properties).

The provider object itself will be created via Unity. Ideally, at the same time I register the provider object, I'd also do something to make-concrete the exact type and configuration of the new O's created.

I could easily do this if I gave the provider access to the Container itself, but that seems wrong.

Alternately, I could provide a Factory Object to the Provider, but that shifts the problem into the Factory - but at least it seems more appropriate for a Factory to access my DI container...

EDIT #1

Ok, I updated the above - my object isn't really a cache - it's a provider of sorts. It's job is to provide a new value when asked. So I have rephrased most of the question and renamed parts of the code. The actual problem hasn't changed, but I believe it answers the question "Why is my cache generating new values anyway?"

If you're confused about why I'm talking about a Cache in this edit, then don't worry - the example I used to describe the problem originally (mis)represented myValueProvider as a Cache.


Solution

  • You are mixing concerns here. It is your cache, its responsibility to cache objects. You are trying to add responsibility to create objects. Object creation may not be straightforward. And each type that there are changes in object creating you will need to update cache class. So your cache should expose methods to cache and retrieve object form cache. Delegate responsibility to create objects to the client code. If you really want this design I would define factory that can create object:

    interface IMyObjectCreater
    {
      IMyinterface Create();
    }
    
    class MyConcreteCreator : IMyObjectCreator
    {
    IMyinterface Create()
    {
    return new MyObject();
    }
    }
    

    and register this factory in Unity container, add constructor argument to cache class:

    public myCache(IMyObjectCreator o)
    {
    }
    

    }