这两种模式看起来都像是控制反转原理的实现.也就是说,一个对象不应该知道如何构造它的依赖关系.
依赖注入(DI)似乎使用构造函数或setter来"注入"它的依赖项.
使用构造函数注入的示例:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
//...
}
Run Code Online (Sandbox Code Playgroud)
服务定位器似乎使用了一个"容器",它连接了它的依赖关系并给它foo吧.
使用服务定位器的示例:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo()
{
this.bar = Container.Get<IBar>();
}
//...
}
Run Code Online (Sandbox Code Playgroud)
因为我们的依赖项只是对象本身,所以这些依赖项具有依赖项,它们具有更多依赖项,依此类推.因此,控制容器的反转(或DI容器)诞生了.示例:Castle Windsor,Ninject,Structure Map,Spring等)
但是,IOC/DI容器看起来完全相同像一个服务定位器.将它称为DI容器是一个坏名字?IOC/DI容器只是另一种服务定位器吗?当我们有很多依赖关系时,我们使用DI容器这一事实的细微差别是什么?
最近我读过Mark Seemann关于Service Locator反模式的文章.
作者指出ServiceLocator为反模式的两个主要原因:
API使用问题(我完全可以使用)
当类使用服务定位器时,很难看到它的依赖关系,因为在大多数情况下,类只有一个PARAMETERLESS构造函数.与ServiceLocator相比,DI方法通过构造函数的参数显式地暴露依赖关系,因此在IntelliSense中很容易看到依赖关系.
维护问题(让我感到困惑)
请考虑以下示例
我们有一个使用服务定位器方法的类'MyType':
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们要为类'MyType'添加另一个依赖项
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我的误解开始的地方.作者说:
要判断你是否引入了一个重大改变,要变得更加困难.您需要了解使用Service Locator的整个应用程序,并且编译器不会帮助您.
但是等一下,如果我们使用DI方法,我们将在构造函数中引入与另一个参数的依赖关系(在构造函数注入的情况下).问题仍然存在.如果我们忘记设置ServiceLocator,那么我们可能忘记在IoC容器中添加新的映射,并且DI方法将具有相同的运行时问题.
此外,作者还提到了单元测试的难点.但是,我们不会有DI方法的问题吗?我们不需要更新所有实例化该类的测试吗?我们将更新它们以传递一个新的模拟依赖项,以使我们的测试可编译.我没有看到更新和时间花费带来任何好处.
我不是想捍卫Service Locator方法.但这种误解让我觉得我失去了一些非常重要的东西.有人可以消除我的怀疑吗?
更新(摘要):
我的问题"服务定位器是反模式"的答案实际上取决于具体情况.我绝对不会建议你从工具列表中删除它.当您开始处理遗留代码时,它可能会变得非常方便.如果你很幸运能够处于项目的最初阶段,那么DI方法可能是更好的选择,因为它比Service Locator有一些优势.
以下是主要的不同之处,这些差异使我不相信我的新项目使用Service Locator:
有关详细信息,请阅读下面给出的优秀答案.
design-patterns dependency-injection anti-patterns service-locator
我正在阅读他的书" 网上依赖注入".
1)他在这里说Bastard Injection只有在我们使用时才会发生Foreign Default.
但在他的书中,第148页上的插图显示,Bastard Injection当依赖项的默认实现是Foreign Default或者Local Default:

那么当依赖的默认实现是一个时,Bastard Injection反模式也会出现Local Default吗?
2)在这里(以及他的书中)他指出,如果一个类有一个可选的依赖,那么这个依赖的默认实现是好的Local Default:
但在下一篇文章中,他似乎反对拥有可选的依赖项,即使默认实现是Local Default:
Run Code Online (Sandbox Code Playgroud)private readonly ILog log; public MyConsumer(ILog log) { this.log = log ??LogManager.GetLogger("My"); }就封装而言,这种方法的主要问题在于,看起来MyConsumer类无论是否控制其日志依赖的创建都无法真正决定.虽然这是一个简化的示例,但如果LogManager返回的ILog实例包装了一个非托管资源,这可能会成为问题,该资源应该在不再需要时处理掉.
当依赖的默认实现是本地的时,他在上面的摘录中的参数是否也有效?如果是这样,那么还应该避免使用具有本地默认值的可选依赖项?
3)Pg.147:
Bastard Injection的主要问题是它使用了FOREIGN DEFAULT ...,我们不能再自由地重用该类了,因为它拖拽了我们可能不想要的依赖.进行并行开发也变得更加困难,因为类很大程度上依赖于它的依赖性.
Foreign Default是一个依赖项的实现,它被用作默认值,并在与其使用者不同的程序集中定义.因此,对于Foreign Default,使用者的程序集也会依赖于依赖项的程序集.
他是否也暗示Foreign Default使并行开发更加困难,而Local Default则不然?如果他是,那么这没有意义,因为我认为使并行开发变得困难的原因并不是消费者的集合很难引用依赖的汇编,而是消费者阶层依赖于一个具体实现的事实.依赖?
谢谢
由于我想使用新的内置依赖注入,我很难找到一种优雅的方法来保持我的DAL层与ASP.NET 5中的MVC/UI层分开.
例如,我有一个ASP.NET 5项目,一个业务层项目和一个数据访问项目,其中我有各种实体框架代码,如实体和上下文.在ASP.NET 5中设置上下文并定位数据库主要文档建议我在StartUp.cs类中执行类似的操作
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<BookContext>(options =>
{
options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
});
Run Code Online (Sandbox Code Playgroud)
这意味着我现在必须在基本上我的UI层中引用我的DAL,根据各种专家和博客文章,多年来一直是不好的做法.
我解决这个问题的一种方法是创建两个新项目,一个是CompositeRoot项目,它包含工厂类来生成我的业务类然后访问DAL,还有一个带有Configuration类的Utilities项目,其中有一个ConnectionString属性,我可以传入我的上下文,然后我使用内置的DI来连接所有内容并避免在我的UI层中引用我的DAL.但我遇到了最新版本的Entity Framework(beta 7)的问题,因为它现在似乎无法在上下文的构造函数或可重写中指定连接字符串OnConfiguration方法.此外,到目前为止,所有文档似乎都不关心这种混合问题.这就是我们现在做事的方式吗?相信开发人员不会做直接在UI中引用DAL类的"坏"事情?或者是否有一种模式,人们正在使用这种新的内置DI /配置为ASP.NET 5保持SOLID?
asp.net dependency-injection entity-framework-core asp.net-core-mvc asp.net-core
Autofac有模块,Windsor有安装程序和StructureMap注册表...使用Simple Injector如何将配置逻辑打包成可重用的类?
我试过了:
public interface IModule { }
public class FooModule : IModule
{
public FooModule(SimpleInjector.Container container)
{
container.RegisterSingleton<IBar, Bar>();
container.RegisterSingleton<IFoo, Foo>();
}
}
Run Code Online (Sandbox Code Playgroud)
我在Composition Root中使用它:
public static void Main(string[] args)
{
var container = new SimpleInjector.Container();
container.RegisterCollection<IModule>(new FooModule(container));
...
}
Run Code Online (Sandbox Code Playgroud)
但是,FooModule取决于容器,可能不是一个好的做法...请参阅http://code.google.com/p/autofac/wiki/BestPractices:
如果组件依赖于容器,请查看它们如何使用容器来检索服务,并将这些服务添加到组件(依赖注入)构造函数参数中.
我一直在从鲍勃叔叔那里读到这篇文章:
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

我有几个问题需要澄清:
谢谢
我个人喜欢从C#代码配置StructureMap的选项.根据我的理解,DI的优点之一是我们可以轻松交换新的具体实例.但是,如果配置是在代码中定义的,那么具体实例在dll中是硬编码的.
所以,实际上,它与硬件编码依赖关系一样好,对吧?我知道,在测试过程中它会让生活更轻松......
我的观点是,使用xml配置不是更好吗?你想插入一个新的具体实例?只需让安装程序用新的文件覆盖structuremap.config文件.
那么,配置StructureMap的首选方法是什么?
额外:我暂时被迫使用C#配置,因为我不知道如何将连接字符串传递给实例.我可以在配置文件中编写连接字符串,但我想重用app.config中定义的连接字符串.
我一直在尝试在asp.net MVC5应用程序中实现一个松散耦合的应用程序.我有一个控制器:
public class HeaderController : Controller
{
private IMenuService _menuService;
public HeaderController(IMenuService menuService)
{
this._menuService = menuService;
}
//
// GET: /Header/
public ActionResult Index()
{
return View();
}
public ActionResult GetMenu()
{
MenuItem menu = this._menuService.GetMenu();
return View("Menu", menu);
}
}
Run Code Online (Sandbox Code Playgroud)
此控制器中使用的服务是:
public class MenuService : IMenuService
{
private IMenuRespository _menuRepository;
public MenuService(IMenuRespository menuRepository)
{
this._menuRepository = menuRepository;
}
public MenuItem GetMenu()
{
return this._menuRepository.GetMenu();
}
}
Run Code Online (Sandbox Code Playgroud)
并且在服务类中使用的存储库是:
public class MenuRepository : IMenuRespository
{
public MenuItem GetMenu()
{ …Run Code Online (Sandbox Code Playgroud) c# asp.net asp.net-mvc dependency-injection inversion-of-control
我正在开展一个侧面项目,以更好地理解控制和依赖注入的反转以及不同的设计模式.
我想知道在工厂和战略模式中使用DI是否有最佳实践?
我的挑战来自于一个策略(从工厂构建)需要为每个可能的构造函数和实现提供不同的参数.结果,我发现自己在服务入口点声明了所有可能的接口,并将它们传递给应用程序.因此,必须针对新的和各种策略类实现更改入口点.
为了便于说明,我在下面汇总了一个配对示例.我的这个项目的堆栈是.NET 4.5/C#和Unity for IoC/DI.
在此示例应用程序中,我添加了一个默认的Program类,负责接受虚构的订单,并根据订单属性和所选的送货提供商计算运费.UPS,DHL和Fedex有不同的计算方法,每个实现可能依赖或不依赖于其他服务(访问数据库,api等).
public class Order
{
public string ShippingMethod { get; set; }
public int OrderTotal { get; set; }
public int OrderWeight { get; set; }
public int OrderZipCode { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
用于计算运费的虚拟计划或服务
public class Program
{
// register the interfaces with DI container in a separate config class (Unity in this case)
private readonly IShippingStrategyFactory _shippingStrategyFactory;
public Program(IShippingStrategyFactory shippingStrategyFactory)
{
_shippingStrategyFactory = shippingStrategyFactory;
}
public …Run Code Online (Sandbox Code Playgroud) c# design-patterns dependency-injection strategy-pattern factory-pattern
我猜测没有办法像Autofac那样做以下内容,为ctor注入一个可枚举的开放泛型类型集合?各种Handle类型都有依赖关系,否则我只是动态构建它们.
class EventOne : IEvent {...}
class EventTwo : IEvent {...}
class EventThree : IEvent {...}
interface IHandleEvent<T> where T : IEvent {...}
class HandleEventOne : IHandleEvent<EventOne> {...}
class HandleEventTwo : IHandleEvent<EventTwo> {...}
class HandleEventThree : IHandleEvent<EventThree> {...}
builder.RegisterAssemblyTypes(myAssembies).AsClosedTypesOf(typeof(IHandleEvent<>));
builder.RegisterType<AService>().As<IAService>();
class AService : IAService
{
public AService(IEnumerable<IHandleEvent<IEvent>> handles)
{...}
}
Run Code Online (Sandbox Code Playgroud) c# ×4
asp.net ×2
.net ×1
architecture ×1
asp.net-core ×1
asp.net-mvc ×1
autofac ×1
oop ×1
structuremap ×1