这段时间以来一直困扰着我,但我找不到正确的答案。
问题:
假设你有一个工厂接口(C#示例):
但是,为什么我们只在
问题:
假设你有一个工厂接口(C#示例):
interface IFooFactory
{
IFoo Create();
}
它的实现取决于一个服务:
class FooFactory : IFooFactory
{
private readonly IBarService _barService;
public FooFactory(IBarService barService)
{
_barService = barService;
}
}
当服务接口实现 IDisposable
接口时,可优雅地关闭服务:
interface IBarService : IDisposable
{
...
}
现在由工厂创建的实际类有两个依赖项 - 服务本身(通过工厂传递),以及由工厂创建的另一个对象:
class Foo : IFoo
{
public Foo(IBarService barService, IQux qux)
{
...
}
}
工厂可以这样创建:
class FooFactory : IFooFactory
{
public IFoo Create()
{
IQux qux = new Qux();
return new Foo(_barService, qux);
}
}
最后,IFoo
和 IQux
都实现了 IDisposable
接口,因此类 Foo
也实现了该接口:
class Foo : IFoo
{
public void Dispose()
{
_qux.Dispose();
}
}
但是,为什么我们只在
Foo.Dispose()
中处理Qux
?虽然两个依赖项都是注入的,但我们只依赖于工厂确切实现的知识,其中Bar
是共享服务(关联关系类型),而Qux
仅由Foo
使用(组合关系类型)。这样很容易错误地处理它们。在这两种情况下,逻辑上Foo
不拥有任何依赖项,因此处理任何一个似乎都是不对的。将Qux
的创建放在Foo
内部会抵消依赖注入,因此这不是一个选项。
有没有更好的方法来同时拥有这两个依赖项,并清楚地表明它们彼此之间的关系,以正确处理它们的生命周期呢?
可能的解决方案。
所以这里有一种可能的不太完美的解决方案:
class FooFactory : IFooFactory
{
private readonly IBarService _barService;
public FooFactory(IBarService barService)
{
_barService = barService;
}
public IFoo Create()
{
// This lambda can capture and use any input argument.
// Also creation can be complex and involve IO.
var quxFactory = () => new Qux();
return new Foo(_barService, quxFactory);
}
}
class Foo : IFoo
{
public Foo(IBarService barService, Func<IQux> quxFactory)
{
// Injected - don't own.
_barService = barService;
// Foo creates - Foo owns.
_qux = quxFactory();
}
public void Dispose()
{
// Now it's clear what Foo owns from the code in the constructor.
_qux.Dispose();
}
}
我不是很喜欢在构造函数中调用可能复杂的逻辑,特别是如果它是async
的话,而且按需(懒加载)调用它也会导致意外的延迟运行时错误(与快速失败相比)。
为了设计而去那么远真的有意义吗?无论如何,我想看看是否有其他可能的优雅解决方案。
Qux
依赖项。我不同意上面的例子,因为我不想(1)过早地处理_barService
和(2)Foo
不拥有任何注入的依赖项,也不应该处理它们——这是一种导致非常危险后果的不良实践。这是我的最大关注点,上面的例子仍然存在这个问题。是的,如果DI框架创建了它,那么它应该被处理。回到Qux
——它必须每次都被创建并绑定到Foo
的生命周期。 - Serge Semenov