我认为接口,多态性和通过依赖注入控制反转等模式的全部原因是为了避免紧耦合,分离,模块化等。
为什么我必须明确地"接线"像ASP.NET中的具体类的接口?不知道我的注册表是一些类型的耦合吗?像,
services.AddTransient<ILogger, DatabaseLogger>();
如果我使用记录器,ILogger,并创建一个实现该接口的文件和数据库类,该怎么办。
在我的IoC中,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public interface ILogger
{
void logThis(string message);
}
public class DatabaseLogger : ILogger
{
public void logThis(string message)
{
//INSERT INTO table.....
System.Console.WriteLine("inserted into a databse table");
}
}
public class FileLogger : ILogger
{
public void logThis(string message)
{
System.Console.WriteLine("logged via file");
}
}
public class DemoClass
{
public ILogger myLogger;
public DemoClass(ILogger myLogger)
{
this.myLogger = myLogger;
}
public void logThis(string message)
{
this.myLogger.logThis(message);
}
}
class Program
{
static void Main(string[] args)
{
DemoClass myDemo = new DemoClass(new DatabaseLogger()); //Isn't this Dependency Injection?
myDemo.logThis("this is a message");
Console.ReadLine();
}
}
}
那么我为什么要注册或者#34;连接&#34;什么?是不是通过构造函数进行了这种依赖注入(我有根本的误解)吗?我可以把任何记录器放在那里实现ILogger。
我会创建其中两个吗?
services.AddTransient<ILogger, DatabaseLogger>();
services.AddTransient<ILogger, FileLogger>();
答案 0 :(得分:3)
你实际上会进一步抽象它。在您的代码中,您将创建一个工厂。
定义用于创建对象的接口,但让子类决定 要实例化的类。工厂方法允许班级推迟 实例化到子类。
所以你本质上会有以下内容:
// Interface:
public interface ILogger : IDisposable
{
void Log<TEntity>(TEntity entity);
}
// Concrete Implementation:
public class DatabaseLogger : ILogger
{
public void Log<TEntity>(TEntity entity)
{
throw new NotImplementedException();
}
}
public class TextLogger : ILogger
{
public void Log<TEntity>(TEntity entity)
{
throw new NotImplementedException();
}
}
使用当前的方式定义,当您将两个依赖项连接到容器时,您将收到以下IEnumerable<ILogger>
。但是,为了正确地包装它,我们将执行以下操作:
public interface ILoggerFactory
{
ILogger CreateDbLogger();
ILogger CreateLogger();
}
public class LoggerFactory : ILoggerFactory
{
public ILogger CreateDbLogger() => new DatabaseLogger();
public ILogger CreateLogger() => new TextLogger();
}
因此,当我们在依赖注入中注册Factory
时,我们只需编写services.AddTransient<ILoggerFactory, LoggerFactory>();
。注入工厂时,您只需使用:
public class Example
{
public ILoggerFactory Factory { get; }
public Example(ILoggerFactory factory)
{
Factory = factory;
}
// Utilize in a method.
using(var logger = Factory.CreateDbLogger())
logger.Log(...);
// Utilize in a method.
using(var logger = Factory.CreateLogger())
logger.Log(...);
}
现在,这可能会引入过度曝光。但希望这澄清了经常使用的依赖注入的用法。您可以通过键入“Factory Pattern”或“Factory Method”来阅读更多内容。
答案 1 :(得分:2)
您的示例也是依赖注入,但是,这是一个非常简单的场景。虽然你可以手动完成它,但它在任何相当大的现实场景中都会失控。
想象一下,你的.navbar{
z-index: 2;
}
有10个依赖项,并且每个依赖项都有自己的一些依赖项,在现实世界的应用程序中很容易就是这种情况。现在想象一下手动实例化所有这些以创建更高级别的服务:
DemoClass
你必须在任何需要DemoClass myDemo = new DemoClass(new DatabaseLogger(new DbLoggerDependency1(), new DbLoggerDependency2(), new DbLoggerDependency3(), new DbLoggerDependency4()), new OtherDemoClassDependency(new OtherDependencyDependency1(), new OtherDependencyDependency2()));
的地方做到这一点。变得丑陋很快吧?这只是一些依赖。
现在想象一下在开发基于代码的基础上开发测试工具时,你必须使用模拟版本重新进行测试,而通过连线,您可以为测试工具引入新的配置。
这种连接可以让您在配置文件中保持所有注入的清洁,分离和可维护性,并允许您轻松地从单一控制点交换实现:
DemoClass
然后你需要services.AddTransient<ILogger, DatabaseLogger>();
services.AddTransient<IDbLoggerDependency1, DbLoggerDependency1>();
services.AddTransient<IDbLoggerDependency2, DbLoggerDependency2>();
services.AddTransient<IDbLoggerDependency3, DbLoggerDependency3>();
services.AddTransient<IDbLoggerDependency4, DbLoggerDependency4>();
services.AddTransient<IOtherDemoClassDependency, OtherDemoClassDependency>();
services.AddTransient<IOtherDependencyDependency1, OtherDependencyDependency1>();
services.AddTransient<IOtherDependencyDependency2, OtherDependencyDependency2>();
services.AddTransient<IDemoClass, DemoClass>();
的任何地方,你可以让你的IoC根据你的配置来实例化并注入正确的依赖关系,这样你就不需要编写所有的样板代码了:
DemoClass
AutoFac等一些容器甚至支持属性注入,因此您甚至不需要包含构造函数。