Search code examples
c#dependency-injectionfactory-pattern

Factory Interface Create Method with object Argument


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);
    }
}

Solution

  • 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