简易IOC容器的设计与实现

最近,接触到了一些.NET平台上的IOC(控制反转)工具,并对依赖注入的概念产生了浓厚的兴趣。尽管这些工具在实际应用中非常有效,但随着时间的推移,它们的复杂性也在增加,这让有些望而却步。为了简化这个过程,决定自己动手实现一个简易的IOC容器。

什么是依赖注入(DI)

根据维基百科的定义,依赖注入是一种设计模式,其核心原则是将行为与依赖解析分离。换句话说,它是一种解耦高度依赖的软件组件的技术。

依赖注入的好处

传统上,开发者习惯于硬编码依赖项,这使得代码紧密耦合,并且随着需求的变化,需要不断地修改代码。这显然违反了DRY(Don't Repeat Yourself)原则,因为开发者可能需要修改整个代码流程或复制方法来支持变化。依赖注入通过实现接口来提供所需的功能,从而帮助解决这个问题。DI框架可以在运行时注入从这个接口继承的对象,从而在可能的情况下提前加载对象。

“ObjectPoolManager”(OPM)框架介绍

这是一个轻量级的依赖注入容器,目前还在开发阶段,但已经能够满足大多数常见的需求。这个框架已经通过了最初设想的一些测试用例。虽然它目前还缺少Ninject和Unity等框架的一些特性,但会随着时间的推移不断更新它。目前,它已经能够帮助减少样板代码。

设计亮点

当前的实现展示了这个DI框架的工作方式。当用户注入类型或对象时,它会在内部的Object容器中维护这些对象,这个容器只能被框架内部访问。目前,这个框架支持有无上下文的池。上下文在这里意味着在该键下注册的类型/对象的目录。这有助于用户在不同的上下文中注册相同的类型,并使用相同的名称。

ObjectPoolManager类

这个类(静态)维护对象池和上下文池:

  • Pool:提供非上下文基础的对象池
  • Context:包含上下文池的对象池集合

方法:

  • Clear:清除对象容器

ObjectPool类

注册和解析对象:

  • Context:上下文池的名称

方法:

  • Register(string, object):将对象注册为提供的字符串名称的单例。
  • Register():注册类类型以使用默认构造函数。
  • Register(ObjectScope):使用指定范围注册类类型以使用默认构造函数。
  • Register(string):使用指定名称注册类类型以使用默认构造函数。
  • Register(string, ObjectScope):使用指定范围和名称注册类类型以使用默认构造函数。
  • Register(Func):使用提供的委托注册类类型。
  • Register(Func, ObjectScope):使用提供的委托和范围注册类类型。
  • Register(string, Func):使用提供的委托和字符串名称注册类类型。
  • Register(string, Func, ObjectScope):使用提供的委托、范围和字符串名称注册类类型。
  • Register():注册类类型T以使用默认构造函数,并将返回类型绑定到I。
  • Register(ObjectScope):使用指定范围注册类类型,并将返回类型绑定到I。
  • Register(string):使用指定名称注册类类型,并将返回类型绑定到I。
  • Register(string, ObjectScope):使用指定范围和名称注册类类型,并将返回类型绑定到I。
  • Register(Func):使用提供的委托注册类类型,并将返回类型绑定到I。
  • Register(Func, ObjectScope):使用提供的委托、范围注册类类型,并将返回类型绑定到I。
  • Register(string, Func):使用提供的委托和字符串名称注册类类型,并将返回类型绑定到I。
  • Register(string, Func, ObjectScope):使用提供的委托、范围和字符串名称注册类类型,并将返回类型绑定到I。
  • Resolve(string):使用指定的字符串名称返回对象。这只能用于使用Register(string, object)注册的对象。
  • Resolve():返回已在当前访问池中注册的类型T的对象。
  • Resolve(string):使用指定的字符串名称返回已在当前访问池中注册的类型T的对象。
  • BeginResolve(string, ObjectInvokeCallback):开始解析使用委托注册的对象。
  • Dispose():释放当前池。

ObjectScope枚举

告诉容器每次调用时创建新对象

  • None:每次调用时创建新对象
  • Singleton:第一次调用后返回相同的对象

ObjectInvokeArgument类

EventArgument返回调用BeginResolve时的对象:

  • Context:回调调用的上下文名称。
  • Name:注册对象类型的名称。
  • Result:异步调用返回的对象。

示例应用程序

public interface IDriveLayout { string Name { get; } } public interface IEngineLayout { string Name { get; } } class RearMidEngine : IEngineLayout { public string Name { get { return "Rear Mid Engine"; } } } 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("RearWheelDrive"); ObjectPoolManager.Pool.Register("RearMidEngine"); ObjectPoolManager.Pool.Register("FerrariF430", () => new FerrariF430( ObjectPoolManager.Pool.Resolve("RearWheelDrive"), ObjectPoolManager.Pool.Resolve("RearMidEngine") ) );

检索类

var vehicle = ObjectPoolManager.Pool.Resolve("FerrariF430"); Console.WriteLine("{0} -> Layout: {1}, {2}", vehicle.Name, vehicle.EngineType, vehicle.DriveType); Console.ReadLine();
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485