2012-07-03 7 views
7

Ho appena iniziato con il Delphi Spring Framework e mi chiedevo se la versione attuale del contenitore DI consentiva in qualche modo di delegare la costruzione a un metodo factory senza specificare un tipo di implementazione?Delphi Spring DI: è possibile delegare l'istanza di interfaccia senza un tipo di implementazione?

E.g. qualcosa di simile a questo:

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

Come si può vedere, il mio caso d'uso specifico è l'istanziazione di oggetti COM. In tal caso, la classe che implementa l'interfaccia a cui sono interessato non fa parte della mia applicazione, ma sono ancora in grado di creare istanze chiamando lo CreateComObject/CoCreateInstance. Tuttavia, sembra che io sia sfortunato in quanto le iscrizioni nel Container sembrano sempre legate a una classe di implementazione effettiva.

Supponendo che al momento non sia possibile, come fareste gli esperti a risolvere questo problema? Creeresti una classe wrapper o una classe fittizia o semplicemente tieni gli oggetti COM fuori dal contenitore DI e li istanzia semplicemente tramite CreateComObject?

risposta

8

Sfortunatamente il design attuale del contenitore DI molla non lo consente. Presuppone internamente che ogni tipo di servizio (di solito l'interfaccia, ma può anche essere una classe) sia implementato da un tipo di componente (una classe). Così avendo TObject in diversi posti in cui avremmo bisogno di IInterface in questo caso. Come il delegato che si sta passando al metodo DelegateTo restituisce il tipo di componente (o TObject nel caso non generico) e non il tipo di servizio.

Questo è anche perché è possibile registrare un tipo di componente con più implementazioni di interfaccia in una sola chiamata dell'interfaccia fluente. Come:

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

Il contenitore ora controlla se TMyObject è compatibile al IMyInterface e IMyOtherInterface. Quando si chiama Resolve il resolver di servizio utilizza GetInterface sull'istanza per ottenere il riferimento all'interfaccia richiesta. Tutto oltre quel punto è fatto su un riferimento a un oggetto.

Poiché ho alcuni piani per il contenitore DI che non richiedono una dipendenza da una classe di implementazione durante la registrazione di interfacce, questo problema verrà risolto in futuro ma non in qualsiasi momento.

Aggiornamento (08.11.2012):

Poiché R522 è possibile registrare i tipi di interfaccia nel modo seguente:

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

In questo esempio si registrerà ISomeObject servizio e qualsiasi interfaccia con un GUID ereditato da.

Inoltre, è possibile aggiungere altre interfacce chiamando lo Implements<T> ma a differenza delle classi non ci sarà alcuna convalida al momento della registrazione se l'istanza costruita supporta realmente tale interfaccia poiché semplicemente non è possibile. Attualmente riceverai nil quando chiami Resolve<T> con un tipo di servizio non supportato. Potrebbe sollevare un'eccezione in futuro.

+3

Grazie per l'aggiornamento! È geniale! :) –

1

Non sembra che l'architettura della struttura a molla attualmente la supporti ma è certamente fattibile. È stato suggested nel gruppo di supporto spring4d e c'è interesse nell'idea.

C'è una classe generica TFactory in Spring.DesignPatterns che può essere utile nel wrapping CreateComObject/COCreateInstance.