65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11投票s)

2010 年 11 月 1 日

CPOL

5分钟阅读

viewsIcon

49281

downloadIcon

545

轻量级且简单

引言

最近我接触了一些适用于 .NET 的 IOC 工具,并且非常喜欢从应用程序的早期阶段就开始进行依赖注入,并在需要时调用/利用它的概念。唯一让我觉得不那么有趣的是,随着时间的推移,它们引入了太多的复杂性。

从概念上讲,这项技术并不难实现,为了好玩,我决定自己做一个。

以下是可用的 .NET IOC 工具列表(按我的偏好顺序)

  1. Ninject
  2. Unity
  3. 温莎
  4. StructureMap

定义

根据维基百科

“面向对象的计算机编程中的依赖注入(DI)是一种设计模式,其核心原则是将行为与依赖解析分离开来。换句话说:一种解耦高度依赖的软件组件的技术。”

优点

传统上,开发人员会在需要时硬编码依赖项,这使得该代码块高度耦合,并且随着时间的推移,如果需求发生变化,就需要进行修改。这显然违反了 DRY 原则,因为开发人员可能需要修改整个代码流程或复制方法来支持该更改。

依赖注入可以通过实现接口来帮助解决这种情况,该接口向最终用户提供所需的功能。DI 框架可以在运行时,在尽可能早的利用之前加载继承自该接口的对象。

DI 框架不必局限于此现象,它实际上克服了开发人员可能遇到的许多常规问题。另一个例子是,开发人员想要利用某个类,但不确定使其可用的输入是什么。在这种情况下,负责的模块可以通过 DI 来处理该类,并让开发人员仅通过引用其名称或类型即可使用其实例。

根据维基百科

依赖注入是控制反转的一种特定形式,其中被反转的关注点是获取所需依赖项的过程。

介绍“ObjectPoolManager” (OPM) 框架

这是一个轻量级的依赖注入容器,目前仍处于开发阶段,但已完成以满足每个 DI 框架都需要实现的绝大多数常见需求。目前,该框架已根据我最初想法中的一些测试用例进行了测试。当然,它缺少 Ninject 和 Unity 的一些功能,但我会随着时间的推移不断升级它。目前,它有助于减少样板代码。

设计亮点

Design.png

上面的模块说明了此 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();

有关更多示例,请参阅源代码中包含的测试用例。

待办事项

  1. 支持构造函数注入和方法注入的属性
  2. 可配置的注册支持(XML 格式)

历史

  • 2010 年 10 月 31 日 – v0.8 Beta 发布
© . All rights reserved.