2012-07-03 27 views
7

Po prostu zaczynam od Delphi Spring Framework i zastanawiałem się, czy obecna wersja kontenera DI w jakiś sposób pozwala na przeniesienie konstrukcji do metody fabrycznej bez określenia typu implementacji?Delphi Spring DI: Czy można delegować instancję interfejsu bez typu implementacji?

E.g. coś podobnego do tego:

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

Jak widać, moim szczególnym przypadkiem użycia jest tworzenie obiektów COM. W takim przypadku klasa implementująca interfejs, który mnie interesuje, nie jest częścią mojej aplikacji, ale nadal mogę tworzyć instancje, dzwoniąc pod numer CreateComObject/CoCreateInstance. Wydaje się jednak, że mam pecha, ponieważ rejestracje w Kontenerze zawsze wydają się być związane z rzeczywistą klasą wykonawczą.

Zakładając, że nie jest to obecnie możliwe, jak byście się rozwinęli? Czy utworzysz klasę opakowania lub klasę dummy, czy po prostu zatrzymasz obiekty COM poza kontenerem DI i po prostu utworzysz je przez CreateComObject?

Odpowiedz

8

Niestety obecna konstrukcja sprężynowego pojemnika DI nie pozwala na to. Wewnętrznie zakłada, że ​​każdy typ usługi (zwykle interfejs, ale może również być klasą) jest implementowany przez typ komponentu (klasa). Tak więc posiadając TObject w kilku miejscach, w których będziemy potrzebować IInterface w tym przypadku. Podobnie jak delegat, który przekazujesz do metody DelegateTo, zwraca typ komponentu (lub TObject w przypadku ogólnym), a nie typ usługi.

Dzieje się tak również dlatego, że można zarejestrować jeden typ komponentu z wieloma implementacjami interfejsu w jednym płynnym wywołaniu interfejsu. Jak:

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

Pojemnik teraz sprawdza czy TMyObject jest zgodny z IMyInterface i IMyOtherInterface. Podczas wywoływania Resolve usługa rozpoznawania nazw używa GetInterface na instancji, aby uzyskać żądane odwołanie do interfejsu. Wszystko poza tym punktem odbywa się na podstawie odniesienia do obiektu.

Ponieważ mam pewne plany dotyczące kontenera DI, który nie wymaga zależności od klasy implementacji podczas rejestrowania interfejsów, problem ten zostanie rozwiązany w przyszłości, ale nie w najbliższym czasie.

Aktualizacja (08.11.2012):

Od r522 można zarejestrować typy interfejsów w następujący sposób:

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

W tym przykładzie będzie to zarejestrować ISomeObject jako usługa i dowolnego interfejsu z identyfikatorem GUID, z którego dziedziczy.

Dodatkowo można dodać inne interfejsy, wywołując Implements<T>, ale w przeciwieństwie do klas nie będzie sprawdzania poprawności w czasie rejestracji, jeśli konstruowana instancja faktycznie obsługuje ten interfejs, ponieważ po prostu nie jest to możliwe. Obecnie otrzymasz nil, dzwoniąc pod numer Resolve<T> z nieobsługiwanym typem usługi. Może w przyszłości spowodować wyjątek.

+3

Dzięki za aktualizację! To genialne! :) –

1

Nie wygląda na to, że architektura wiosennego szkieletu obecnie obsługuje tę funkcję, ale jest z pewnością wykonalna. Zostało suggested w grupie wsparcia spring4d i istnieje zainteresowanie tą ideą.

Istnieje ogólna klasa TFactory w Spring.DesignPatterns, która może być przydatna do zawijania CreateComObject/COCreateInstance.