是否可以在没有任何第三方工具的情况下进行 DI?在他们发现一些 DI 框架之前,我已经读过有人用抽象类和接口来做这件事。ID是如何以这种非常基本的形式完成的?
5 回答
实例化时只需将依赖项传递给类的构造函数即可。当项目很小(低于几千行代码)时,不需要 DI 框架 - 您可以编写一个工厂并手动连接所有依赖项。
本教程中有一个很好的描述。
基本上你所做的就是让DependingClass
唯一知道一个Interface
,然后你用你的IndependentClass
. 使用构造函数重载,您可以让例如单元测试框架发送模拟对象。
一些代码可能会让我更容易理解我的意思:
public interface IAnInterface
{
void MethodOne();
void MethodTwo();
}
public class IndependentClass : IAnInterface
{
// Implements all members of IAnInterface
}
public class DependentClass
{
private IAnInterface _dependency;
public DependentClass() : this(new IndependentClass()) { }
public DependentClass(IAnInterface dependency)
{
this._dependency = dependency;
}
}
现在,如您所见,我们提供了一个默认类类型,如果没有向构造函数提供参数,它将被实例化。但是我们也允许注入实现相同接口的不同类或相同的模拟对象。
编辑:正如评论中所指出的,在较大的应用程序中,最好有一个实例化 的工厂DependingClass
,并删除“默认”构造函数。这样,如果您决定更改实施,您只需在一个地方进行更改。
当然,没有第三方工具也是可能的。简单示例:
interface ILogger
{
void Log(string text);
}
class SomeClass
{
private ILogger _logger;
public SomeClass(ILogger logger)
{
_logger = logger;
}
public void DoSomeWork()
{
Log("Starting");
// do work
Log("Done");
}
private void Log(string text)
{
if (_logger != null)
{
_logger.Log(text);
}
}
}
SomeClass
在构造函数中接受一个ILogger
作为输入。它使用它来记录一些输出。假设我们希望在控制台中使用它:
class ConsoleLogger : ILogger
{
public void Log(string text)
{
Console.WriteLine(text);
}
}
在一些代码中:
SomeClass instance = new SomeClass(new ConsoleLogger());
instance.DoSomeWork();
..但是我们希望将日志记录在一个文件中:
class FileLogger : ILogger
{
private string _fileName;
public FileLogger(string fileName)
{
_fileName = fileName;
}
public void Log(string text)
{
File.AppendAllText(_fileName, text);
}
}
因此,我们改为注入文件记录器:
SomeClass instance = new SomeClass(new FileLogger("path to file"));
instance.DoSomeWork();
SomeClass
很高兴不知道ILogger
正在使用的实现,而只是使用注入的任何实现。让工厂创建接口实现的实例而不是在整个代码中构建对象通常是一个好主意,以便更简单地更改正在使用的实现。
有三种方法可以做到...
在构造函数中传递对依赖实例(当然是实现接口的类的实例)的引用。
public class MyClass { private readonly ISpillDAL iSpDal; public ISpillDAL SpillDal { get { return iSpDal; } } public SpillLogic() : this(null) { } public SpillLogic(ISpillDAL splDAL) { iSpDal = splDAL ?? new SpillDAL(); // this allows a default } }
创建新对象,然后通过属性设置器将引用传递给依赖对象
public class MyClass { private readonly ISpillDAL iSpDal; public ISpillDAL SpillDal { set { iSpDal = value; } get { return iSpDal; } } public SpillLogic() { } }
在接受引用并将其分配给您为此创建的内部 provbate 变量的对象中使用一个函数
public class MyClass { private readonly ISpillDAL iSpDal; public ISpillDAL SpillDal { set { iSpDal = value; } get { return iSpDal; } } public SpillLogic() { } public void InjectSpillDAL( ISpillDAL splDAL ) { iSpDal = splDAL; } }
您可以创建通过接口相互通信的组件,并让您的托管程序实例化组件并将它们链接在一起。
这将是您的解决方案结构:
一个 dll 程序集,它定义了组件之间的契约(接口 + 数据对象,它们是接口方法签名的一部分)。
一个或多个定义组件(实现接口)的 dll 程序集。组件之间的任何通信都是通过接口完成的。
一个 exe 程序集,它启动宿主进程,实例化组件并链接它们设置一些属性。每当您需要替换一个组件时,您只需更改此项目。
您可以为任何组件创建单元测试,模拟您正在测试的组件使用的组件。
您还可以从托管项目的 app.confing 文件中读取属性绑定。