Это довольно простой сценарий шаблона декоратора с усложнением, которое у декорированного типа есть параметр конструктора, который зависит от типа, в который он вставляется.
У меня есть такой интерфейс:
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
требуется другое значение configuration
для передачи конструктору его 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
) уровне?
Любые другие комментарии?