Это довольно простой сценарий шаблона декоратора с той сложностью, что декорированный тип имеет параметр конструктора, который зависит от типа, в который он внедряется.
У меня есть такой интерфейс:
interface IThing
{
void Do();
}
И такая реализация:
class RealThing : IThing
{
public RealThing(string configuration)
{
... implementation ...
}
public void Do()
{
... implementation ...
}
}
И такой декоратор:
class DecoratingThing : IThing
{
IThing _innerThing;
public DecoratingThing(IThing thing)
{
_innerThing = thing;
}
public void Do()
{
_innerThing.Do();
}
}
Наконец, у меня есть несколько типов, для которых требуется IThing
, называемый Depender1
, Depender2
и т. Д.
class DependerX()
{
public DependerX(IThing thing)
{
... implementation ...
}
}
Я хочу настроить контейнер IOC для разрешения экземпляров DependerX
таким образом, чтобы в них вводился RealThing
, украшенный DecoratingThing
. Важно: Каждый DependerX
тип требует передачи в конструктор его RealThing
разных значений конфигурации
, например: " ConfigX "в каждом конкретном случае. например Контейнер IoC может выполнять следующие действия:
new Depender1(new DecoratingThing(new RealThing("Config1")));
new Depender2(new DecoratingThing(new RealThing("Config2")));
... и так далее.
В Unity это кажется довольно неуклюжим для настройки, поскольку мне приходится смешивать декоратор с декорированным:
container.RegisterType<IThing, DecoratingThing>("ConfigX",
new InjectionFactory(container => new DecoratingThing(new RealThing("ConfigX"));
container.RegisterType<DependerX>(
new InjectionConstructor(new ResolvedParameter<IThing>("ConfigX");
И повторяю, аккуратно нарушая DRY для каждого DependerX
.
Я бы хотел избавиться от необходимости встраивать конструкцию RealThing
в конструкцию DecoratingThing
в каждую именованную регистрацию IThing
- и заявить украшение всего один раз. Это так, например, что если в будущем нужно изменить украшение, его легче перенастроить. Лучшее, что я придумал, - это вспомогательный метод регистрации:
void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
container.RegisterType<TDepender>(new InjectionConstructor(
new ResolvedParameter<IThing>(config)));
container.RegisterType<IThing, DecoratingThing>(config,
new InjectionFactory(c => new DecoratingThing(new RealThing(config))));
}
По крайней мере, он удаляет повторение, но мне все равно нужно встроить конструкцию RealThing
в DecoratingThing
- это означает, например, что я не могу изменять их продолжительность жизни независимо друг от друга. Я не могу зарегистрировать IThing
снова, чтобы сделать это, потому что я израсходовал свою регистрацию этого интерфейса для имени.Если я хочу это сделать, мне нужно ввести еще один набор именованных экземпляров, например:
void RegisterDepender<TDepender>(IUnityContainer container, string config)
{
string realConfig = "Real" + config;
container.RegisterType<TDepender>(new InjectionConstructor(
new ResolvedParameter<IThing>(config)));
container.RegisterType<IThing, DecoratingThing>(config,
new InjectionFactory(c => new DecoratingThing(
container.Resolve<IThing>(realConfig))));
container.RegisterType<IThing, RealThing>(realConfig,
new ContainerControlledLifetimeManager(),
new InjectionConstructor(config));
}
Это действительно лучший вариант? Это сложно и потенциально трудно для тех, кто придет после грока. Есть ли у других контейнеров IoC убедительный способ охватить этот сценарий? Поскольку шаблон того, как работает внедрение, повторяется для каждого DependerX, есть ли способ использовать только именованный экземпляр на верхнем ( DependerX
) уровне?
Любые другие комментарии?