我有以下具有几个依赖项的基类:
public abstract class ViewModel
{
private readonly ILoggingService loggingService;
public ViewModel(
ILoggingService loggingService,
...)
{
this.loggingService = loggingService;
...
}
}
在我的派生类中,我不想重复这个基类构造函数中的所有参数,所以我这样做了:
public abstract class ViewModel
{
private readonly IUnityContainer container;
private ILoggingService loggingService;
...
public ViewModel(IUnityContainer container)
{
this.container = container;
}
public ILoggingService LoggingService
{
get
{
if (this.loggingService == null)
{
this.loggingService = this.container.Resolve<IUnityContainer>();
}
return this.loggingService;
}
}
...
}
现在我的派生类只需要传递一个东西给我的基类构造函数,我还得到了一个很好的效果,那就是只有在需要的时候才解析依赖项。
然而,我后来认识到传递IOC容器是一个坏主意,最好的替代设计模式是什么,记住传递的许多服务都是作为单例在IOC容器中注册的?
9条答案
按热度按时间jdzmm42g1#
正如你所说的,你应该避免传递容器,这会把它变成一个“收纳袋”,在那里你不再能看到你的依赖项是什么,也不容易看到袋子里有什么。
相反,如果你发现你的构造函数带了太多的参数,这本身就是一种气味,在这种情况下,你经常会发现你的类试图做太多的事情(这违反了单一责任原则)。
看看你的参数列表,看看你是否可以把参数分成更小的组,例如,如果你的构造函数接受
IEmailSender
,IEventLog
和ILoggingService
,也许你真正需要的是一个聚合这三个依赖项的INotificationService
。当然,有时候你的构造函数确实有那么多的依赖项。在这种情况下,类可能只是用来把这些东西聚集在一起并把它们连接起来。如果是这样的话,类可能应该避免做任何实际的工作。
yizd12fk2#
在构造函数中传递所有依赖项是最干净的方法。
我看不出在派生类中传递参数有什么问题。避免类型化是错误的动机,有像Resharper这样的工具可以帮助你生成这些构造函数。
如果有很多依赖项,则表明该类违反了单一责任模式。
在很多情况下,支持组合而不是继承也是一个好主意,这也有助于将类拆分成更小的部分,而不会违反SRP。
qni6mghb3#
如果你想在一系列类之间保持一致的行为,AOP可能是一个解决方案。http://www.sharpcrafters.com/solutions/logging
但是,对于临时日志记录,这可能并不完全适合您的需要。
axkjgtzd4#
当你有很多层次的继承时,这可能是一个麻烦,但是显式地告诉类有什么依赖项是一件好事(通过构造函数)。一个替代方法是Annotating Objects for Property (Setter) Injection,但是我建议你只对可选的依赖项使用它,例如logger。
qzwqbdag5#
避免传递容器。这是服务位置。你必须反转控制,这样无论什么创建你的ViewModel,都会给你依赖的日志服务。
Mark Seeman在他的书中很好地做到了这一点。AOP是一个整洁的替代方案,正如有人已经强调的那样。
您的代码应变为:
x759pob26#
CommonServiceLocator可以为您提供一种通过静态调用解析资源的方法。
Unity docs Using Injection Attributes展示了构造函数注入不灵活时的其他方法。
当我有一个公共基类时,我喜欢使用Property setter注入进行日志记录:
我不能说我曾经实际使用过方法调用注入。
你不应该仅仅因为你不能设计所有的东西都让所有的依赖项总是通过构造函数进来就觉得自己做错了什么......在一个完美的世界里也许是这样,但在实践中有选择是很好的......
oprakyz77#
只需通过容器创建派生类,并在需要的地方注入它们。
错误示例-
Foo
担心Bar
的依赖性,因为它需要示例化Bar
。正确示例-
Foo
不关心如何创建Bar
。Bar
直接从容器获取依赖项。yhqotfr88#
最后,我使用工厂模式创建了下面的接口和类,以减少添加到构造函数的参数数量:
在我的IOC容器中,我注册了一次IInfrastructure工厂,在我的视图模型中,我只有一个依赖项,创建一个新的视图模型要快得多,也简单得多。
vlurs2pr9#
你应该坚持你的第一个模式。如果你厌倦了添加那些构造函数变量,那么你已经有太多了。考虑一下把你的类分解成更小的部分。这个模式是如此强大,因为它可以通过懒惰来自我调节:)
如果你有一个全局类型依赖,你可能想在任何地方使用(日志记录是一个完美的例子)......只需使用单例模式......(注意我还假设容器也是使用单例模式创建的)。
那就像这样...
这样你就不用把它注射到所有东西里了。
通常应该避免使用这种模式,除非在很多模块中使用它...