0

I have a question about creating a factory interface with a create method that can cater for accepting different argument types depending on the implementation.

To give you a bit more background, I am using dependency in injection in a project, and require stateful objects to be generated at runtime - therefore I am injecting factories (rather than the objects themselves) to create these stateful objects. The problem I have come across is that, for some interfaces, the concrete implementations simply cannot have the same constructor argument types, and so the factories that create an instance of these interfaces require almost 'dynamic' arguments to be passed to the create method.

I have been going over this for a couple of days, and the following is the best solution I could come up with (namely, passing an object to the factory create method and casting it in the concrete implementation of the factory). I am really looking for feedback from people who have come across this scenario before, to hear what they came up with, and whether or not the solution I am proposing below is acceptable.

Apologies if this is missing any information, and many thanks in advance!

//
// Types...
//

interface IDataStore
{
    List<string> GetItems();
}

public class XmlDataStore : IDataStore
{
    public XmlDataStore(XmlDocument xmlDoc)
    {
        // Initialise from XML Document...
    }

    public List<string> GetItems()
    {
        // Get Items from XML Doc...
    }
}

public class SQLDataStore : IDataStore
{
    public SQLDataStore(SqlConnection conn)
    {
        // Initialise from SqlConnection...
    }

    public List<string> GetItems()
    {
        // Get Items from Database Doc...
    }
}

//
// Factories...
//

interface IDataStoreFactory
{
    IDataStore Create(object obj);
}

class XmlDataStoreFactory : IDataStore
{
    IDataStore Create(object obj)
    {
        // Cast to XmlDocument
        return new XmlDataStore((XmlDocument)obj);
    }
}

class SQLDataStoreFactory : IDataStore
{
    IDataStore Create(object obj)
    {
        // Cast to SqlConnection
        return new SQLDataStore((SqlConnection)obj);
    }
}
Orinoco
  • 3
  • 1

2 Answers2

1

Not really sure if I understand your question correctly but to me it sounds a little odd to have factory instances which you use for the creation of your statefull objects as you call them.

To directly answer your question: generics are your solution. You rinterface becomes an open generic abstraction:

interface IDataStore<TStoreType>
{
    List<string> GetItems();
}

interface IDataStoreFactory<TStoreType>
{
    IDataStore<TStoreType> Create(TStoreType obj);
}

and your factory classes will look like this:

class XmlDataStoreFactory : IDataStoreFactory<XmlDocument>
{
    IDataStore<XmlDocument> Create(XmlDocument document)
    {
        return new XmlDataStore(document);
    }
}

class SQLDataStoreFactory : IDataStoreFactory<SqlConnection>
{
    IDataStore<SqlConnection> Create(SqlConnection connection)
    {
        return new SQLDataStore(connection);
    }
}

This will work, but from the examples you give I got the impression you're using factories throughout your codebase. Maybe I'm wrong on this point, but look at your design and minimize the number of factories. Needing a factory means mixing data with behaviour and this will always, eventually, get you into trouble.

For example, let's say you have some kind of service which adds the current user to a audit log when he logs in. This service offcourse needs the current user which is a typical example of runtime data (or contextual data). But instead of:

public class AuditLogService
{
    public void AddApplicationSignIn(User user)
    {
        //... add user to some log
    }
}

I know this is not a good example because you actually wouldn't need a factory for this class, but with the next code example you'll get the point:

public class AuditLogService
{
    private readonly IUserContext userContext;        

    public AuditLogService(IUserContext userContext)
    {
        this.userContext = userContext;
    }

    public void AddApplicationSignIn()
    {
        var user = this.userContext.GetCurrentUser();
         //... add user to some log
    }
}

So by splitting data from behaviour you rule out the need for factories. And admitted there are cases where a factory is the best solution. I do think an IDataStore is not something you need a factory for.

For a good blog on splitting data and behaviour read here

Ric .Net
  • 5,540
  • 1
  • 20
  • 39
  • This post probably best explains the scenario as to why I am injecting factories rather than the objects themselves... http://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-container/1945023#1945023 – Orinoco Oct 24 '14 at 10:05
  • Well that answer is correct, offcourse. I got (still get) the impression from your examples your misusing this answer to use factories throughout your code base. See my addition in the answer why this is a problem and how you can solve this. – Ric .Net Oct 24 '14 at 10:41
  • Ric, thanks for your efforts, however unfortunately I don't think generics are the solution in this case - The factories being injected are assigned as members of the class that they are being injected into, which as far as I can see in you're solution means you would have to know the TStoreType at compile time (i.e. when defining the factory members of the class), which I don't, unless I am missing something in your answer? I cannot summarise the complexities of what I am working on in an SO post, so you'll have to take my word that weeks of testing / research led me to injecting factories. – Orinoco Oct 24 '14 at 12:39
  • Ok, I'll accept that... Generics are your solution. I'll post another answer, as that will be a different approach. – Ric .Net Oct 24 '14 at 14:04
1

Based on this comment you need one factory which produces several types of IDataStore. You could accomplish by creating a open generic factory method in the singleton factory instance.

interface IDataStore<TStoreType> 
{
    void SetBaseType(TStoreType obj);
    List<string> GetItems();
}

interface IDataStoreFactory
{
    IDataStore<TStoreType> Create<TStoreType>(TStoreType obj) 
}

class DataStoreFactory : IDataStoreFactory
{
    public IDataStore<TStoreType> Create<TStoreType>(TStoreType obj)
    {
        if (obj.GetType() == typeof(SqlConnection))
        {
            var store = new SQLDataStore((SqlConnection)(Object)obj);
            return (IDataStore<TStoreType>)store;
        }
        if (obj.GetType() == typeof(XmlDocument))
        { //... and so on }
    }
}

class SQLDataStore : IDataStore<SqlConnection>
{
    private readonly SqlConnection connection;
    public SQLDataStore(SqlConnection connection)
    {
        this.connection = connection;
    }

    public List<string> GetItems() { return new List<string>(); }
}

You can use this factory like this:

var factory = new DataStoreFactory();
var sqlDatastore = factory.Create(new SqlConnection());
var xmlDatastore = factory.Create(new XmlDocument());


Your datastore factory would become a lot less complex if you would use a DI container. You could inject the container in the factory and retrieve your instances directly from the container, which would typically build your instances from bottom to top, including there own dependencies, lifetime management and so on. But be very carefull with this approach, it is the first step to using the service locator pattern which is an anti pattern

Community
  • 1
  • 1
Ric .Net
  • 5,540
  • 1
  • 20
  • 39
  • I think we've (almost) gone full circle here, in that the concrete implementation of the factory interface is effectively casting the generic type (passed to the create method) to the specific type required. I think my original concern was that the 'casting' (i.e. in my example casting an object to a particular type) would be frowned upon. I feel I should mark this as the answer due to your efforts (and the fact it is a valid answer), however if ultimately we end up casting I may stick with my approach as it fits in with the way I am mocking objects when unit testing. – Orinoco Oct 24 '14 at 16:12
  • I can follow your thinking about going full circle. But there are two big differences. You now have one factory instead of a factory per datastore type (the factories in that case are actually factory methods in its own class). Your type castings are now in one place, namely your factory. All datastore implementation are plain and simple and you could create them from anywhere using type safe constructors (opposed to your previous 'object'). If you would ever use a DI container you could remove the castings and this change is needed only in your factory class == compliant with open/close – Ric .Net Oct 24 '14 at 20:08