我喜欢构造函数注入依赖注入。它强制从类型中明确声明问题,并有助于可测试性。
我喜欢构造函数注入,在大多数位置......
记录我不喜欢的例子。如果我有一个基类,许多其他类继承,我希望所有这些类都使用我的ILogger(或其他)的实例,我不想要一个静态工厂(Logger.Instance)...我不希望必须在每个带有ILogger的子类上声明构造函数。
所以,我可以让我的基类将logger声明为Property并让它以这种方式注入
public class MyBaseClass
{
public ILogger Logger { get; set; }
}
......但是
那么......我还有其他选择吗? (我正在使用Castle Windsor)。
我打算制作一个界面
public interface IInitializable<T>
{
void Initialize(T instance);
}
public class MyBaseClass : IInitializable<ILogger>, ...could have other IInitializables too...
{
protected ILogger Logger { get; private set; }
public void Initialize(ILogger instance)
{
Logger = instance;
}
}
然后在我的容器上有一个工具,在类型构造时自动调用IInitializable<T>
的所有实现......
但在我走这条路之前,我想知道其他人的想法是什么......
答案 0 :(得分:3)
你这太复杂了。 recommended and documented pattern to inject ILogger将NullLogger.Instance
作为默认值(即null object pattern),并使Logger
成为可选的依赖项。为记录器设置公共设置器没有任何问题。使用您展示的自定义IInitializable
可能只会使事情变得复杂并且没有任何实际价值。
我将从此处的文档中复制样本以供参考:
using Castle.Core.Logging;
public class CustomerService
{
private ILogger logger = NullLogger.Instance;
public CustomerService()
{
}
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
// ...
}
编辑:似乎问题实际上是根据上下文(与原始问题几乎没有关系)实现不同的记录器实现。如果是这种情况,请使用服务覆盖或处理程序选择器。
答案 1 :(得分:1)
在你的情况下,我会使用属性注入。
属性注入可以切换为强制,如下所述: http://www.mail-archive.com/castle-project-users@googlegroups.com/msg08163.html
答案 2 :(得分:0)
如果您的基类本身不依赖于ILogger
,则应从基类中删除该属性。这样就可以保持基类构造函数的清洁。
否则,您可以创建一个能够创建MyBaseClass
后代的工厂。这可能如下所示:
public interface IMyBaseClassFactory
{
MyBaseClass CreateNew<TMyBaseClass>() where TMyBaseClass : MyBaseClass;
}
现在,您可以创建一个IMyBaseClassFactory
实现的寄存器,该实现能够创建新实例并注册可选依赖项:
public MyBaseClassFactoryImpl : IMyBaseClassFactory
{
public MyBaseClass CreateNew<TMyBaseClass>()
{
// Call your IoC container to get a descendant.
var instance = ServiceLocator.GetInstance<TMyBaseClass>();
// Register missing dependencies
instance.Logger = ServiceLocator.GetInstance<ILogger>();
return instance;
}
}
大多数IoC容器允许您使用属性修饰可注入属性。然而,使用工厂的优点是,您的应用程序将完全不知道所使用的IoC框架。作为一个缺点,还有更多工作要做(创建一个工厂,注入该工厂而不是实例本身,并调用工厂来创建一个新实例)。
定义injectable属性时,需要进行读/写。当然,您不希望任何人在事后意外重置该属性。没有构造函数注入,很难通过编译时支持实现这一点。但是,很容易进行运行时检查:
private ILogger logger;
public ILogger Logger
{
get { return this.logger; }
set
{
if (this.logger != null)
{
throw new InvalidOperationException("Logger has already been set.");
}
this.logger = value;
}
}
我希望这会有所帮助。