Есть это программное обеспечение, X, у которого есть этот действительно сложный API, для которого я должен написать фасад. Я написал библиотеку классов XClientLibrary
и создал ее с использованием контейнера DI и IoC (Unity). Это стало возможным, потому что моя библиотека экспортирует сервисы (интерфейсы), поэтому пользователи не знают о конкретных классах, которые используют конструктор DI. Они также не знают о контейнере IoC.
«Корневая служба» - это экземпляр IXClient
, который предполагается создать один раз и использовать до тех пор, пока приложение работает. (Кстати, это настольное приложение). X-клиент позволяет пользователям подключаться к X-хостам, если они знают URL. Х-хост позволяет пользователям получать доступ к сервисам хоста, их сервисам и т. Д. (Довольно сложный граф объектов). Это пример кода пользователя:
// 1. app startup
XClientProvider provider = new XClientProvider(); // call only once per app
IXClient xClient = provider.GetClient(); // always returns the same instance
xClient.Startup();
// 2. app normal usage
IXHost host = xClient.ConnectToHost(new Uri("http://localhost")); // return new instance each time
IXService1 service = host.GetThis();
IXService2 otherService = service.DoThat();
...
host.Dispose();
// get another host, consume it, dispose it, etc
...
// 3. app shutdown
xClient.Shutdown();
provider.Dispose();
Я пытался следовать рекомендациям Марка Симанна, чтобы реализовать это, но я не уверен, применимы ли они к библиотеке классов. Поставщик клиента - это корень композиции, который является единственным местом, где используется контейнер IoC. Корень композиции следует шаблону RRR :
- контейнер создан на
new XClientProvider()
и настроен
- контейнер разрешает
IXClient
при вызове GetClient()
- контейнер расположен на
provider.Dispose()
Все усложняется, когда контейнер запрашивается для разрешения IXHost
. Его реализация:
internal class XHost : IXHost
public XHost(Uri uri, IXService1 service1)
Клиент должен создавать XHost
экземпляров, поэтому его реализация должна знать, как создавать IXService1
:
internal class XClient : IXClient
public XClient(Func<IXService1> xService1DelegateFactory)
Вызов фабрики делегатов достигает контейнера, который создает IXService1
. Также предположим, что на этом графике есть класс XComponent7
, для которого требуется точный экземпляр IXService1
, который использовался для создания хоста:
internal class XComponent7 : IXService7
public XComponent7(Func<IXService1> service1DelegateFactory)
Мне нужно использовать Func
, чтобы справиться с циклической зависимостью. Контейнер должен быть настроен так, чтобы после разрешения IXService1
он предоставлял один и тот же экземпляр всякий раз, когда запрашивается разрешение IXService1
.
.
Теперь все становится действительно сложно. Я хочу ограничить это поведение «разрешением каждого узла», то есть после создания узла контейнер должен создать IXService1
и кэшировать его и предоставлять его любому компоненту, который ему нужен, если компонент является частью графа объектов. гостья. Мне также нужен способ утилизации всех компонентов при удалении хоста.
Я думал, что смогу сделать это, используя дочерние контейнеры. Я могу создать его, когда пользователи звонят по номеру ConnectToHost
, просят его разрешить хост и утилизировать его. Основной контейнер все еще жив и не будет удален, пока они не вызовут Dispose
на провайдере.
Проблема в том, что я думаю, что это нарушает шаблон RRR. Поэтому мне интересно, как работает RRR, когда задействованы дочерние контейнеры ... Может быть, IXHost
- это еще один "корень", который может быть напрямую разрешен с помощью корня композиции? Или, может быть, есть действительно умный менеджер времени жизни Unity, который может делать то, что мне нужно?