10

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?

Rick
  • 5,029
  • 2
  • 33
  • 34

1 Answers1

10

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
  • 225,310
  • 48
  • 427
  • 736
  • 2
    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 Feb 02 '10 at 14:01
  • 1
    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 Feb 02 '10 at 14:30
  • 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 Feb 02 '10 at 15:06
  • Registering the Factory with unity, still leaves the IAcmeServcice types unresolved and thus this code fails?!. Is this right? – brumScouse Nov 03 '11 at 17:06
  • As given here, you'd also need to register the disparate IAcmeServivce implementation with Unity, since they are injected into the factory. – Mark Seemann Nov 03 '11 at 18:34
  • @Mark anyway to get rid of if else in the factory? should we even try to do that? – Kalpesh Soni Aug 25 '14 at 20:01
  • @KalpeshSoni There are various options for doing that. The simplest is to use a Dictionary instead. Perhaps some of these options are useful as well: http://blog.ploeh.dk/2013/01/09/MetadataRoleHint, http://blog.ploeh.dk/2013/01/10/RoleInterfaceRoleHint, http://blog.ploeh.dk/2013/01/11/PartialTypeNameRoleHint – Mark Seemann Aug 26 '14 at 08:04
  • 1
    Generally the factory examples I have seen create new objects (this one happens to take 3 of them as an arg!) If I use a mapping, then I only get that object, not a new one - the dictionary or detached metadata approach has to store an object, which means, to make my methods threadsafe I cant have any properties in it – Kalpesh Soni Aug 26 '14 at 19:07