Orinoco
Orinoco

Reputation: 3

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

Upvotes: 0

Views: 848

Answers (2)

Ric .Net
Ric .Net

Reputation: 5550

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

Upvotes: 1

Ric .Net
Ric .Net

Reputation: 5550

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

Upvotes: 1

Related Questions