views:

75

answers:

1

I have an interface (call it IAcmeService) that has multiple implementations.

FileSystemAcmeService
DatabaseAcmeService
NetworkAcmeService

The end-user needs to be able to select which implementation will be used and also save that selection.

Currently I'm configuring my IOC container (Unity) to register all the known implemenatation with a name.

container.RegisterType(of IAcmeService, FileSystemAcmeService)("FileSystemAcmeService")
container.RegisterType(of IAcmeService, DatabaseAcmeService)("DatabaseAcmeService")
container.RegisterType(of IAcmeService, NetworkAcmeService)("NetworkAcmeService")

To allow the user to save their selection I have app.config configuration section file that stores the chosen service name to use.

To resolve the selected implementation I'm doing a manual Resolve in the Initialize method of the class the uses the service.

Private _service as IAcmeService
Public Sub Initialize()
    _service = container.Resolve(of IAcmeService)(_config.AcmeServiceName)
End Sub

This doesn't seem right because my class has to know about the container. But I can't figure out another way.

Are there other ways to allow end-user selection without the class knowing about the container?

+1  A: 

Defining and implementing and Abstract Factory is the standard solution to this type of problem. If you will pardon my use of C#, you can define an IAcmeServiceFactory interface like this:

public interface IAcmeServiceFactory
{
    IAcmeService Create(string serviceName);
}

You can now write a concrete implementation like this one:

public class AcmeServiceFactory : IAcmeServiceFactory
{
    private readonly IAcmeService fsService;
    private readonly IAcmeService dbService;
    private readonly IAcmeService nwService;

    public AcmeServiceFactory(IAcmeService fsService,
        IAcmeService dbService, IAcmeService nwService)
    {
        if (fsService == null)
        {
            throw new ArgumentNullException("fsService");
        }
        if (dbService == null)
        {
            throw new ArgumentNullException("dbService");
        }
        if (nwService == null)
        {
            throw new ArgumentNullException("nwService");
        }

        this.fsService = fsService;
        this.dbService = dbService;
        this.nwService = nwService;
    }

    public IAcmeService Create(string serviceName)
    {
        switch case serviceName
        {
            case "fs":
                return this.fsService;
            case "db":
                return this.dbService;
            case "nw":
                return this.nwService;
            case default:
                throw new ArgumentException("serviceName");
        }
    } 
}

You can make it more general-purpose if you want to be able to create an arbitrary number of IAcmeService instances, but I will leave that as an exercise to the reader :)

This will require you to register the Factory with Unity as well. In any place where you need an IAcmeService based on a name, you take a dependency on IAcmeServiceFactory instead of IAcmeService itself.

Mark Seemann
Ah...I almost missed the fact that the factory constructor has each of the concrete services. Which allows the container to construct the instances. Nice.The one downside I see with this pattern is the expense of creating all the instances when only one will be needed.I'm going to give this a shot and see how it performs. Thanks Mark for the well written answer.
Rick
You can solve performance issues (if any) using Lazy loading of the services as described here: http://blog.ploeh.dk/2010/01/20/RebuttalConstructorOverinjectionAntipattern.aspx However, often services will/should be long-lived objects (using the Singleton lifetime style), in which case it doesn't matter at all.
Mark Seemann
Right. I'm also thinking that my factory could take a dependency on the configuration object. That would remove the need for the configuration object from the class using the service.
Rick
Sounds reasonable :)
Mark Seemann