使用 ObjectPoolManager (OPM) 进行依赖注入





5.00/5 (11投票s)
轻量级且简单
引言
最近我接触了一些适用于 .NET 的 IOC 工具,并且非常喜欢从应用程序的早期阶段就开始进行依赖注入,并在需要时调用/利用它的概念。唯一让我觉得不那么有趣的是,随着时间的推移,它们引入了太多的复杂性。
从概念上讲,这项技术并不难实现,为了好玩,我决定自己做一个。
以下是可用的 .NET IOC 工具列表(按我的偏好顺序)
- Ninject
- Unity
- 温莎
- StructureMap
定义
根据维基百科
“面向对象的计算机编程中的依赖注入(DI)是一种设计模式,其核心原则是将行为与依赖解析分离开来。换句话说:一种解耦高度依赖的软件组件的技术。”
优点
传统上,开发人员会在需要时硬编码依赖项,这使得该代码块高度耦合,并且随着时间的推移,如果需求发生变化,就需要进行修改。这显然违反了 DRY 原则,因为开发人员可能需要修改整个代码流程或复制方法来支持该更改。
依赖注入可以通过实现接口来帮助解决这种情况,该接口向最终用户提供所需的功能。DI 框架可以在运行时,在尽可能早的利用之前加载继承自该接口的对象。
DI 框架不必局限于此现象,它实际上克服了开发人员可能遇到的许多常规问题。另一个例子是,开发人员想要利用某个类,但不确定使其可用的输入是什么。在这种情况下,负责的模块可以通过 DI 来处理该类,并让开发人员仅通过引用其名称或类型即可使用其实例。
根据维基百科
依赖注入是控制反转的一种特定形式,其中被反转的关注点是获取所需依赖项的过程。
介绍“ObjectPoolManager” (OPM) 框架
这是一个轻量级的依赖注入容器,目前仍处于开发阶段,但已完成以满足每个 DI 框架都需要实现的绝大多数常见需求。目前,该框架已根据我最初想法中的一些测试用例进行了测试。当然,它缺少 Ninject 和 Unity 的一些功能,但我会随着时间的推移不断升级它。目前,它有助于减少样板代码。
设计亮点

上面的模块说明了此 DI 框架的当前实现。每当用户注入类型或对象时,它都会将其保存在 Object
容器中,该容器仅供框架内部访问。目前,该框架支持带上下文和不带上下文的池。这里的上下文简单来说就是在一个键下注册的类型/对象的目录。这有助于用户在不同的上下文中注册相同的类型,甚至具有相同的名称。
ObjectPoolManager 类
(静态
) 维护对象池和池的上下文
属性
Pool |
提供非上下文对象池 |
背景 |
包含对象池的上下文集合 |
方法
Clear |
清除对象容器 |
ObjectPool Class
注册和解析对象
属性
背景 |
所属池的上下文名称 |
方法
Register(string, object) |
将对象注册为提供的字符串名称的单例。 |
Register<T>() |
为默认构造函数注册类类型。 |
Register<T>(ObjectScope) |
使用指定的范围为默认构造函数注册类类型。 |
Register<T>(string) |
使用指定的名称为默认构造函数注册类类型。 |
Register<T>(string, ObjectScope) |
使用指定的范围字符串名称为默认构造函数注册类类型。 |
Register<T>(Func<T>) |
使用提供的委托注册类类型。 |
Register<T>(Func<T>, ObjectScope) |
使用提供的委托和范围注册类类型。 |
Register<T>(string, Func<T>) |
使用提供的委托和字符串名称注册类类型。 |
Register<T>(string, Func<T>, ObjectScope) |
使用提供的委托、范围和字符串名称注册类类型。 |
Register<I, T>() |
为默认构造函数注册类类型 T ,并将其返回类型绑定到 I 。 |
Register<I, T>(ObjectScope) |
使用指定的范围为默认构造函数注册类类型,并将其返回类型绑定到 I 。 |
Register<I, T>(string) |
使用指定的名称为默认构造函数注册类类型,并将其返回类型绑定到 I 。 |
Register<I, T>(string, ObjectScope) |
使用指定的范围字符串名称为默认构造函数注册类类型,并将其返回类型绑定到 I 。 |
Register<I, T>(Func<T>) |
使用提供的委托注册类类型,并将其返回类型绑定到 I 。 |
Register<I, T>(Func<T>, ObjectScope) |
使用提供的委托和范围注册类类型,并将其返回类型绑定到 I 。 |
Register<I, T>(string, Func<T>) |
使用提供的委托和字符串名称注册类类型,并将其返回类型绑定到 I 。 |
Register<I, T>(string, Func<T>, ObjectScope) |
使用提供的委托、范围和字符串名称注册类类型,并将其返回类型绑定到 I 。 |
Resolve(string) |
使用指定的字符串名称返回对象。这只能用于使用 Register(string, object) 注册的对象。 |
Resolve<T>() |
返回在当前访问的池中已注册的类型为 T 的对象。 |
Resolve<T>(string) |
使用指定的字符串名称返回在当前访问的池中已注册的类型为 T 的对象。 |
BeginResolve<T>(string, ObjectInvokeCallback) |
开始解析使用委托注册的对象。 |
Dispose() |
处置当前池。 |
ObjectScope 枚举
|
指示容器在每次调用时创建一个新对象。 |
|
第一次调用后返回同一对象。 |
ObjectInvokeArgument Class
调用 ObjectInvokeCallback
上的 BeginResolve
时返回的 EventArgument
。
属性
背景 |
调用回调的上下文名称。 |
名称 |
用于注册对象类型的名称。 |
结果 |
从异步调用返回的对象。 |
示例应用程序
考虑以下实现法拉利 F430 的 RR 布局的示例。
public interface IDriveLayout
{
string Name { get; }
}
public interface IEngineLayout
{
string Name { get; }
}
class RearMidEngine : IEngineLayout
{
public string Name
{
get { return "Rear Mid Engine"; }
}
}
public class RearWheelDrive : IDriveLayout
{
public string Name
{
get { return "Rear Wheel Drive"; }
}
}
public class Vehicle
{
private IDriveLayout _driveLayout;
private IEngineLayout _engineLayout;
public string DriveType
{
get { return _driveLayout.Name; }
}
public string EngineType
{
get { return _engineLayout.Name; }
}
public virtual string Name
{
get { return "Vehicle"; }
}
public Vehicle(IDriveLayout driveLayout, IEngineLayout engineLayout)
{
_driveLayout = driveLayout;
_engineLayout = engineLayout;
}
}
class FerrariF430 : Vehicle
{
public FerrariF430(IDriveLayout driveLayout, IEngineLayout engineLayout)
: base(driveLayout, engineLayout)
{ }
public override string Name
{
get
{
return "Ferrari F430";
}
}
}
注册类
ObjectPoolManager.Pool.Register<IDriveLayout, RearWheelDrive>("RearWheelDrive");
ObjectPoolManager.Pool.Register<IEngineLayout, RearMidEngine>("RearMidEngine");
ObjectPoolManager.Pool.Register<Vehicle, FerrariF430>("FerrariF430", () =>
new FerrariF430(
ObjectPoolManager.Pool.Resolve<IDriveLayout>("RearWheelDrive"),
ObjectPoolManager.Pool.Resolve<IEngineLayout>("RearMidEngine")
));
检索类
var vehicle = ObjectPoolManager.Pool.Resolve<Vehicle>"FerrariF430");
Console.WriteLine("{0} -> Layout: {1}, {2}",
vehicle.Name, vehicle.EngineType, vehicle.DriveType);
Console.ReadLine();
有关更多示例,请参阅源代码中包含的测试用例。
待办事项
- 支持构造函数注入和方法注入的属性
- 可配置的注册支持(XML 格式)
历史
- 2010 年 10 月 31 日 – v0.8 Beta 发布