如何使用 IOC 容器构造对象
How to construct objects with an IOC container
我相信我对依赖注入有足够的了解,可以开始使用它,但我在理解 IOC 容器与服务位置以及如何使用容器构建我的对象时遇到了困难。
给定:
public interface IFooService
{
void DoSomethingFooey();
}
public class FooService : IFooService
{
readonly IBarService _barService;
public FooService(IBarService barService)
{
this._barService = barService;
}
public void DoSomethingFooey()
{
// some stuff
_barService.DoSomethingBarey();
}
}
public interface IBarService
{
void DoSomethingBarey();
}
public class BarService : IBarService
{
readonly IBazService _bazService;
public BarService(IBazService bazService)
{
this._bazService = bazService;
}
public void DoSomethingBarey()
{
// Some more stuff before doing ->
_bazService.DoSomethingBazey();
}
}
public interface IBazService
{
void DoSomethingBazey();
}
public class BazService : IBazService
{
public void DoSomethingBazey()
{
Console.WriteLine("Blah blah");
}
}
如果没有 IOC 容器,我将不得不像这样在构造时提供我的所有依赖项:
public void DoStuff()
{
// Without a container, I would have to do something yucky like:
FooService fs = new FooService(
new BarService(
new BazService()
)
);
// or
BazService bazService = new BazService();
BarService barService = new BarService(bazService);
FooService fooService = new FooService(barService);
}
通过这种 DI 方式构建我的 类 我似乎收获了很多,因为我现在可以独立测试我的 类,而在使用 DI 之前我真的不能。
但是根据我正在阅读的有关进行 DI 的 "proper way" 的内容,我会使用像 Unity 或 StructureMap 这样的 IOC 容器...但我还没有找到我需要了解的确切内容开始吧。
我假设我的容器看起来像这样:
var container = new Container(_ =>
{
_.For<IFooService>().Use<FooService>();
_.For<IBarService>().Use<BarService>();
_.For<IBazService>().Use<BazService>();
});
取自以下示例:http://structuremap.github.io/quickstart/
根据上面的示例,我不确定包含 var container...
的方法的签名是什么样的,或者它是如何被调用并保持在范围内的。也许更重要的是如何使用容器实际构建对象。
如果我想创建一个 FooService
的实例(然后 BarService
和 BazSerivce
),我的 DoStuff()
现在会是什么样子?
之前是:
public void DoStuff()
{
// Without a container, I would have to do something yucky like:
FooService fs = new FooService(
new BarService(
new BazService()
)
);
// or...
}
但是现在使用我的容器会是什么样子?我的方法如何知道在哪里寻找我的容器?
希望我走在正确的轨道上,但如果我不是,请告诉我,以及我遗漏了什么。
我通常这样做是为了保留 DI 并避免 IOC 容器的缺点:
public interface IFooService
{
void DoSomethingFooey();
}
public class FooService : IFooService
{
readonly IBarService _barService;
public FooService() : this(new BarService()) {}
public FooService(IBarService barService)
{
this._barService = barService;
}
public void DoSomethingFooey()
{
// some stuff
_barService.DoSomethingBarey();
}
}
public interface IBarService
{
void DoSomethingBarey();
}
public class BarService : IBarService
{
readonly IBazService _bazService;
public BarService() : this(new BazService()) {}
public BarService(IBazService bazService)
{
this._bazService = bazService;
}
public void DoSomethingBarey()
{
// Some more stuff before doing ->
_bazService.DoSomethingBazey();
}
}
public interface IBazService
{
void DoSomethingBazey();
}
public class BazService : IBazService
{
public void DoSomethingBazey()
{
Console.WriteLine("Blah blah");
}
}
修改为使用容器的 DoStuff 方法如下所示:
public void DoStuff()
{
IFooService fooService = container.GetInstance<IFooService>();
// do something with fooService
}
我基于您引用的 StructureMap link。其他 IoC 容器以不同方式命名其等效 "GetInstance" 方法。
您通常不会在尝试获取 IFooService
实例的同一方法中创建 container
。通常,您会在某些初始化方法中的其他地方配置您的容器,并将容器保存在您可以轻松访问的地方,例如在静态 属性 中。因此,您的 DoStuff 方法可能看起来更像这样:
public void DoStuff()
{
IFooService fooService = MyIoCContainer.Current.Getinstance<IFooService>();
// do something with fooService
}
其中MyIoCContainer
只是你定义的class,它的Current
属性是你在初始化时配置的StructureMap容器
但是,您并不总是需要编写这样的代码才能获得服务。一些框架提供了钩子来使用你的 IoC 容器,这样你就可以在你的 classes 中继续使用依赖注入,并且框架负责使用你的 IoC 容器实例化你的 classes。
仅举一个例子,ASP.NET MVC 框架提供了一个 IDependencyResolver
接口和 DependencyResolver
class,您可以使用它来允许该框架在以下情况下使用您的 IoC 容器在您的 Web 应用程序中实例化您的控制器 classes。这样,您就可以继续使用依赖注入一直到您的控制器中,因此根本不需要在您的控制器中显式引用您的 IoC 容器。请参阅 this section on "Including a Custom Dependency Resolver" 了解这是如何完成的。
因此,理想情况下,您的 DoStuff
方法及其包含的 class 看起来像这样,这只是依赖注入的进一步延续。那么问题是这个 MyStuffDoer
class 是如何被实例化的:要么通过一个了解如何使用你的 IoC 容器来创建它的框架,要么通过你在某处编写一些显式代码以从 IoC 容器实例化它.
class MyStuffDoer
{
IFooService fooService;
public MyStuffDoer(IFooService fooService)
{
this.fooService = fooService;
}
public void DoStuff()
{
// do something with fooService
}
}
我相信我对依赖注入有足够的了解,可以开始使用它,但我在理解 IOC 容器与服务位置以及如何使用容器构建我的对象时遇到了困难。
给定:
public interface IFooService
{
void DoSomethingFooey();
}
public class FooService : IFooService
{
readonly IBarService _barService;
public FooService(IBarService barService)
{
this._barService = barService;
}
public void DoSomethingFooey()
{
// some stuff
_barService.DoSomethingBarey();
}
}
public interface IBarService
{
void DoSomethingBarey();
}
public class BarService : IBarService
{
readonly IBazService _bazService;
public BarService(IBazService bazService)
{
this._bazService = bazService;
}
public void DoSomethingBarey()
{
// Some more stuff before doing ->
_bazService.DoSomethingBazey();
}
}
public interface IBazService
{
void DoSomethingBazey();
}
public class BazService : IBazService
{
public void DoSomethingBazey()
{
Console.WriteLine("Blah blah");
}
}
如果没有 IOC 容器,我将不得不像这样在构造时提供我的所有依赖项:
public void DoStuff()
{
// Without a container, I would have to do something yucky like:
FooService fs = new FooService(
new BarService(
new BazService()
)
);
// or
BazService bazService = new BazService();
BarService barService = new BarService(bazService);
FooService fooService = new FooService(barService);
}
通过这种 DI 方式构建我的 类 我似乎收获了很多,因为我现在可以独立测试我的 类,而在使用 DI 之前我真的不能。
但是根据我正在阅读的有关进行 DI 的 "proper way" 的内容,我会使用像 Unity 或 StructureMap 这样的 IOC 容器...但我还没有找到我需要了解的确切内容开始吧。
我假设我的容器看起来像这样:
var container = new Container(_ =>
{
_.For<IFooService>().Use<FooService>();
_.For<IBarService>().Use<BarService>();
_.For<IBazService>().Use<BazService>();
});
取自以下示例:http://structuremap.github.io/quickstart/
根据上面的示例,我不确定包含 var container...
的方法的签名是什么样的,或者它是如何被调用并保持在范围内的。也许更重要的是如何使用容器实际构建对象。
如果我想创建一个 FooService
的实例(然后 BarService
和 BazSerivce
),我的 DoStuff()
现在会是什么样子?
之前是:
public void DoStuff()
{
// Without a container, I would have to do something yucky like:
FooService fs = new FooService(
new BarService(
new BazService()
)
);
// or...
}
但是现在使用我的容器会是什么样子?我的方法如何知道在哪里寻找我的容器?
希望我走在正确的轨道上,但如果我不是,请告诉我,以及我遗漏了什么。
我通常这样做是为了保留 DI 并避免 IOC 容器的缺点:
public interface IFooService
{
void DoSomethingFooey();
}
public class FooService : IFooService
{
readonly IBarService _barService;
public FooService() : this(new BarService()) {}
public FooService(IBarService barService)
{
this._barService = barService;
}
public void DoSomethingFooey()
{
// some stuff
_barService.DoSomethingBarey();
}
}
public interface IBarService
{
void DoSomethingBarey();
}
public class BarService : IBarService
{
readonly IBazService _bazService;
public BarService() : this(new BazService()) {}
public BarService(IBazService bazService)
{
this._bazService = bazService;
}
public void DoSomethingBarey()
{
// Some more stuff before doing ->
_bazService.DoSomethingBazey();
}
}
public interface IBazService
{
void DoSomethingBazey();
}
public class BazService : IBazService
{
public void DoSomethingBazey()
{
Console.WriteLine("Blah blah");
}
}
修改为使用容器的 DoStuff 方法如下所示:
public void DoStuff()
{
IFooService fooService = container.GetInstance<IFooService>();
// do something with fooService
}
我基于您引用的 StructureMap link。其他 IoC 容器以不同方式命名其等效 "GetInstance" 方法。
您通常不会在尝试获取 IFooService
实例的同一方法中创建 container
。通常,您会在某些初始化方法中的其他地方配置您的容器,并将容器保存在您可以轻松访问的地方,例如在静态 属性 中。因此,您的 DoStuff 方法可能看起来更像这样:
public void DoStuff()
{
IFooService fooService = MyIoCContainer.Current.Getinstance<IFooService>();
// do something with fooService
}
其中MyIoCContainer
只是你定义的class,它的Current
属性是你在初始化时配置的StructureMap容器
但是,您并不总是需要编写这样的代码才能获得服务。一些框架提供了钩子来使用你的 IoC 容器,这样你就可以在你的 classes 中继续使用依赖注入,并且框架负责使用你的 IoC 容器实例化你的 classes。
仅举一个例子,ASP.NET MVC 框架提供了一个 IDependencyResolver
接口和 DependencyResolver
class,您可以使用它来允许该框架在以下情况下使用您的 IoC 容器在您的 Web 应用程序中实例化您的控制器 classes。这样,您就可以继续使用依赖注入一直到您的控制器中,因此根本不需要在您的控制器中显式引用您的 IoC 容器。请参阅 this section on "Including a Custom Dependency Resolver" 了解这是如何完成的。
因此,理想情况下,您的 DoStuff
方法及其包含的 class 看起来像这样,这只是依赖注入的进一步延续。那么问题是这个 MyStuffDoer
class 是如何被实例化的:要么通过一个了解如何使用你的 IoC 容器来创建它的框架,要么通过你在某处编写一些显式代码以从 IoC 容器实例化它.
class MyStuffDoer
{
IFooService fooService;
public MyStuffDoer(IFooService fooService)
{
this.fooService = fooService;
}
public void DoStuff()
{
// do something with fooService
}
}