问题描述
这是一个非常简单的装饰器模式方案,复杂的是装饰的类型具有一个构造函数参数,该参数取决于要注入的类型。
This is a fairly straight forward decorator pattern scenario, with the complication that the decorated type has a constructor parameter that is dependent on the type into which it is being injected.
我有一个这样的接口:
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
等。
Finally, I have some types that require an IThing
, called Depender1
, Depender2
etc..
class DependerX()
{
public DependerX(IThing thing)
{
... implementation ...
}
}
我要配置IOC容器来解析 DependerX
的实例,以便为它们注入 RealThing
并装饰有 DecoratingThing
。 重要:每个 DependerX
类型需要不同的配置
传递给其 RealThing
的构造函数,在每种情况下都说 ConfigX。例如IoC容器完成的工作可能是:
I want to configure an IOC container to resolve instances of DependerX
such that they are injected with RealThing
decorated with a DecoratingThing
. Important: Each DependerX
type requires a different value of configuration
to be passed to the constructor of its RealThing
, say "ConfigX" in each case. e.g. The work done by the IoC container might be:
new Depender1(new DecoratingThing(new RealThing("Config1")));
new Depender2(new DecoratingThing(new RealThing("Config2")));
...等等。
在Unity中,这配置起来很笨拙,因为我必须在装饰器中与装饰器混合使用:
In Unity, this seems quite clunky to configure as I have to mix in the decorator with the decorated:
container.RegisterType<IThing, DecoratingThing>("ConfigX",
new InjectionFactory(container => new DecoratingThing(new RealThing("ConfigX"));
container.RegisterType<DependerX>(
new InjectionConstructor(new ResolvedParameter<IThing>("ConfigX");
然后重复,很好地违反了DRY ,对于每个 DependerX
。
And repeat, violating DRY nicely, for each DependerX
.
我想做的就是消除嵌入 RealThing
在 IThing
的每个命名注册中构造 DecoratingThing
的构造-并只声明一次装饰,例如,这样,如果将来需要更改装饰,那么重新配置就更容易了,我想到的最好的就是这种registra辅助方法
What I'd like to do is remove the need to embed the construction of RealThing
in the construction of DecoratingThing
in each named registration of IThing
- and declare the decoration just once. This is so, for example, that if the decoration needs to change in future, it's easier to reconfigure. The best I came up with is this helper method for registration:
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))));
}
这至少消除了重复,但是我仍然必须嵌入 DecoratingThing
中的 RealThing
-例如,这意味着我无法独立更改其寿命。我无法再次注册 IThing
来执行此操作,因为我已经用完该接口的名称注册了。如果我想这样做,我必须引入另一组这样的命名实例:
This removes repetition at least, but I still have to embed the construction of the RealThing
inside the DecoratingThing
- this means I can't vary their lifetimes independently for example. I can't register IThing
again to do this because I've used up my registration of that interface for the name. If I want to do that I have to introduce another set of named instances like so:
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
)级别使用命名实例?
Is this really the best option? It feels complex and potentially hard for those that will come after to grok. Do other IoC containers have a compelling way to cover this scenario? Since the pattern for how injection works is repeated for each DependerX, is there a way to only use a named instance at the top (DependerX
) level?
还有其他评论吗?
推荐答案
类设计本身似乎是合理的。这是一个基于基于公约的容器配置,基本上可以做到这一点:
The class design itself seems reasonable. Here's a convention-based container configuration that basically does this:
public class MyConventions : UnityContainerExtension
{
protected override void Initialize()
{
var dependers = from t in typeof(IThing).Assembly.GetExportedTypes()
where t.Name.StartsWith("Depender")
select t;
foreach (var t in dependers)
{
var number = t.Name.TrimStart("Depender".ToArray());
var realName = "Real" + number;
var decoName = "Deco" + number;
var config = "Config" + number;
this.Container.RegisterType<IThing, RealThing>(realName,
new InjectionConstructor(config));
this.Container.RegisterType<IThing, DecoratingThing>(decoName,
new InjectionConstructor(
new ResolvedParameter<IThing>(realName)));
this.Container.RegisterType(t,
new InjectionConstructor(
new ResolvedParameter<IThing>(decoName)));
}
}
}
此配置将自动添加所有与上述谓词相匹配的类,因此,一旦设置好,就可以添加更多类(例如 Depender4
或 Depender5 $ c
This configuration will automatically add all classes that match the above predicate, so once you've set it up, you can just add more classes (like Depender4
or Depender5
) without revisiting the container configuration at all.
上面的配置满足以下单元测试:
The above configuration satisfies these unit tests:
[Fact]
public void ContainerCorrectlyResolvesDepender1()
{
var container = new UnityContainer().AddNewExtension<MyConventions>();
var actual = container.Resolve<Depender1>();
var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
Assert.Equal("Config1", thing.Configuration);
}
[Fact]
public void ContainerCorrectlyResolvesDepender2()
{
var container = new UnityContainer().AddNewExtension<MyConventions>();
var actual = container.Resolve<Depender2>();
var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
Assert.Equal("Config2", thing.Configuration);
}
[Fact]
public void ContainerCorrectlyResolvesDepender3()
{
var container = new UnityContainer().AddNewExtension<MyConventions>();
var actual = container.Resolve<Depender3>();
var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
Assert.Equal("Config3", thing.Configuration);
}
这篇关于配置Unity以解析具有修饰的依赖项的类型,该修饰的依赖项的参数随注入的类型而异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!