2010-02-01 12 views
10

Tengo una interfaz (llámalo IAcmeService) que tiene múltiples implementaciones.¿Cómo resolver el tipo según el valor de configuración del usuario final?

FileSystemAcmeService 
DatabaseAcmeService 
NetworkAcmeService 

El usuario final tiene que ser capaz de seleccionar qué aplicación se utilizará y también guardar esa selección.

Actualmente estoy configurando mi contenedor IOC (Unity) para registrar todas las implementaciones conocidas con un nombre.

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

Para permitir al usuario guardar su selección tengo fichero sección de configuración app.config que almacena el nombre del servicio elegido utilizar.

Para resolver la implementación seleccionada estoy haciendo una resolución manual en el método Initialize de la clase que utiliza el servicio.

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

Esto no parece correcto porque mi clase tiene que saber sobre el contenedor. Pero no puedo entender otra forma.

¿Hay otras maneras de permitir la selección del usuario final sin que la clase sepa sobre el contenedor?

Respuesta

9

Definir e implementar y Abstract Factory es la solución estándar para este tipo de problema. Si se me permite mi uso de C#, se puede definir una interfaz IAcmeServiceFactory así:

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

ahora se puede escribir una aplicación concreta como ésta:

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

Usted puede hacer que sea más general- propósito si desea poder crear un número arbitrario de instancias de IAcmeService, pero lo dejaré como un ejercicio para el lector :)

Esto requerirá que registre la fábrica con Unity también. En cualquier lugar donde necesite un IAcmeService basado en un nombre, toma una dependencia de IAcmeServiceFactory en lugar de IAcmeService.

+2

Ah ... Casi echo de menos el hecho de que el constructor de la fábrica tiene cada uno de los servicios concretos. Que permite que el contenedor construya las instancias. Bonito. La única desventaja que veo con este patrón es el gasto de crear todas las instancias cuando solo se necesitará una. Voy a dar una oportunidad y ver cómo funciona. Gracias Mark por la respuesta bien escrita. – Rick

+1

Puede resolver problemas de rendimiento (si los hubiera) utilizando la carga diferida de los servicios como se describe aquí: http://blog.ploeh.dk/2010/01/20/RebuttalConstructorOverinjectionAntipattern.aspx Sin embargo, a menudo los servicios serán/deberían ser de larga duración objetos (utilizando el estilo de vida de Singleton), en cuyo caso no importa en absoluto. –

+0

Derecha. También estoy pensando que mi fábrica podría tomar una dependencia del objeto de configuración. Eso eliminaría la necesidad del objeto de configuración de la clase que usa el servicio. – Rick

Cuestiones relacionadas