2012-07-03 15 views
7

Estoy empezando con Delphi Spring Framework y me preguntaba si la versión actual del contenedor DI de alguna manera permite delegar la construcción a un método de fábrica sin especificar un tipo de implementación.Delphi Spring DI: ¿Es posible delegar instancias de interfaz sin un tipo de implementación?

E.g. algo similar a esto:

GlobalContainer 
    .RegisterFactory<ISomeObject>(
    function: ISomeObject 
    begin 
     Result := CreateComObject(CLASS_SomeObject) as ISomeObject; 
    end) 
    .Implements<ISomeObject> // could probably be implied from the above 
    .AsSingletonPerThread; 

Como puede ver, mi caso de uso específico es la instanciación de objetos COM. En ese caso, la clase que implementa la interfaz que me interesa no es parte de mi aplicación, pero aún puedo crear instancias llamando al CreateComObject/CoCreateInstance. Sin embargo, parece que no tengo suerte ya que los registros en el Contenedor siempre parecen estar vinculados a una clase de implementación real.

Asumiendo que esto no es posible como tal en este momento, ¿cómo podrían resolverlo los expertos? ¿Crearías una clase contenedora o una clase ficticia o simplemente mantendrías los objetos COM fuera del contenedor DI y simplemente los crearías a través del CreateComObject?

Respuesta

8

Desafortunadamente, el diseño actual del contenedor DI de primavera no lo permite. Asume internamente que cada tipo de servicio (normalmente interfaz, pero también puede ser una clase) se implementa por un tipo de componente (una clase). Por lo tanto, teniendo TObject en varios lugares donde necesitaríamos IInterface en este caso. Al igual que el delegado que está pasando al método DelegateTo, devuelve el tipo de componente (o TObject en el caso no genérico) y no el tipo de servicio.

Eso también se debe a que puede registrar un tipo de componente con múltiples implementaciones de interfaz en una sola llamada de interfaz fluida. Al igual que:

GlobalContainer 
    .RegisterType<TMyObject> 
    .Implements<IMyInterface> 
    .Implements<IMyOtherInterface>; 

El contenedor ahora comprueba si TMyObject es compatible con IMyInterface y IMyOtherInterface. Al llamar al Resolve, el servicio de resolución usa GetInterface en la instancia para obtener la referencia de interfaz solicitada. Todo más allá de ese punto se hace en una referencia de objeto.

Como tengo algunos planes para el contenedor DI que requieren no tener una dependencia en una clase de implementación al registrar interfaces, este problema se resolverá en el futuro, pero no en el futuro cercano.

Actualización (08/11/2012):

Desde R522 es posible registrar los tipos de interfaz de la siguiente manera:

GlobalContainer 
    .RegisterType<ISomeObject> 
    .DelegateTo(
    function: ISomeObject 
    begin 
     Result := CreateComObject(CLASS_SomeObject) as ISomeObject; 
    end) 
    .AsSingletonPerThread; 

En este ejemplo se registrará como ISomeObject servicio y cualquier interfaz con un GUID heredado de.

Además, puede agregar otras interfaces llamando al Implements<T> pero, a diferencia de las clases, no habrá validación en el momento del registro si la instancia construida en realidad realmente admite esa interfaz, ya que simplemente no es posible. Actualmente obtendrá nil al llamar al Resolve<T> con un tipo de servicio no admitido. Puede generar una excepción en el futuro.

+3

¡Gracias por la actualización! ¡Eso es brillante! :) –

1

No parece que la arquitectura del marco de primavera actualmente lo admite, pero es ciertamente factible. Ha sido suggested en el grupo de soporte spring4d y hay interés en la idea.

Hay una clase genérica TFactory en Spring.DesignPatterns que puede ser útil en el embalaje CreateComObject/COCreateInstance.

Cuestiones relacionadas