ESBasic 可复用的.NET类库(14) -- 优先级管理器 IPriorityManager
来源:互联网 发布:淘宝宠物用品好卖吗 编辑:程序博客网 时间:2024/04/20 11:17
1.缘起:
假设我们的订单处理系统所要处理的订单是有优先级的,也就是说,不同的订单类型所要求被处理的紧迫程度不同,对那些优先级高的注单要先处理,对于优先级低的注单可稍后处理。对于处于同一优先级的订单了,就按照其到达的先后顺序进行处理。
这是一个典型的管理具有优先级的对象的需求,注单就是具有优先级(With Priority)的对象。我设计了ESBasic.ObjectManagement.Managers.IPriorityManager优先级管理器(确切地说,应该称之为“具有优先级对象的管理器”)来对类似的对象进行管理。
优先级管理器的形象示意图如下:
2.适用场合:
如果你的系统需要对被管理的对象进行优先级分级,并满足以下条件,则可使用IPriorityManager:
(1)对象需要按优先级别(PriorityLevel)进行分类。
(2)优先级别的划分是固定的,不会随系统的运行而发生变化。
(3)对处于同一优先级别的对象,采用先来后到的顺序进行“第二优先级”的高低确定。
(4)优先级别可以使用>=0的连续整数来表示。
3.设计思想与实现
在前面的叙述中,具有优先级对象的管理器的功能职责是相当清晰明了的,在进入其实现之前,首先我们要解决的一个问题是,如何对处于同一优先级别的对象进行管理。根据前面的需求描述,如果两个对象处于相同的优先级别,则先到达的对象的优先程度(即所谓的“第二优先级”)更高。
我使用ESBasic.ObjectManagement.Managers.ISamePriorityObjectManager(同一优先级别对象管理器)来管理属于同一优先级别的所有对象,其接口定义如下:
{
/// <summary>
/// AddWaiter 添加一个等待者。如果等待者在管理器中已经存在,则直接返回。
/// </summary>
void AddWaiter(T waiter);
/// <summary>
/// Count 当前管理器中等待者的数量。
/// </summary>
int Count { get; }
/// <summary>
/// GetNextWaiter 返回等待时间最长的waiter。
/// 注意,返回时并不会从等待列表中删除waiter。如果要删除某个等待者,请调用RemoveWaiter。
/// </summary>
T GetNextWaiter();
/// <summary>
/// GetWaitersByPriority 按照等待者加入的先后顺序返回等待者数组,数组中index越小的等待者其等待时间越长,其优先级也越高。
/// </summary>
T[] GetWaitersByPriority();
/// <summary>
/// RemoveWaiter 从管理器中移除指定的等待者。
/// </summary>
void RemoveWaiter(T waiter);
/// <summary>
/// Clear 清空管理器中的所有等待者。
/// </summary>
void Clear();
/// <summary>
/// Contains 管理器中是否存在指定的等待者。
/// </summary>
bool Contains(T waiter);
}
在ISamePriorityObjectManager所表述的语义环境中,被管理的对象称为“等待者”waiter――这表示一个对象等待被处理。
关于SamePriorityObjectManager的实现,有以下几点需要说明:
(1)其内部是使用LinkedList而不是Queue来存储等待者的,其主要原因在于SamePriorityObjectManager需要支持移除管理器中任一等待者的RemoveWaiter方法。由于Queue本身不支持任意位置的删除功能,所以我使用了LinkedList。新加入的等待者将被放在LinkedList的最后位置。
(2)当管理器中没有任何等待者时,GetNextWaiter方法将返回default(T),如果T是值类型,则此时GetNextWaiter返回的可能并不是一个你所期望的对象。所以,如果T是值类型,在调用GetNextWaiter之前先访问一下其Count属性确保管理器中还有等待者存在。
(3)SamePriorityObjectManager使用了前面介绍的SmartRWLocker来对内部的waiterList进行读写锁控制。
在讨论完SamePriorityObjectManager的实现以后,我们将注意力转移到本节的主角IPriorityManager上来,IPriorityManager的接口定义如下:
/// IPriorityManager 具有优先级的对象的管理器。
/// </summary>
/// <typeparam name="T">被管理的对象的类型,必须从IPriorityObject继承。</typeparam>
public interface IPriorityManager<T> : ISamePriorityObjectManager<T> where T : class, IPriorityObject
{
int PriorityLevelCount { get; set; }
}
IPriorityManager接口直接从ISamePriorityObjectManager继承,并没有多加任何方法,唯一增加的就是一个PriorityLevelCount属性和要求被管理的对象的类型必须是从IPriorityObject接口继承的一个泛型约束。
PriorityLevelCount用于设定你的系统需要有几种优先级别。比如,我的订单基于紧急的优先级可分为紧急、普通、不紧急三种,那么就可将PriorityLevelCount属性设置为3。
一个类型从IPriorityObject接口继承,就表明它的实例是具有优先级属性的对象。
/// IPriorityObject 具有优先级的对象的接口。
/// </summary>
public interface IPriorityObject
{
int PriorityLevel { get; }
}
为什么ISamePriorityObjectManager没有要求被管理的对象继承自IPriorityObject接口了?这是因为在ISamePriorityObjectManager的职责中,其仅仅是根据对象的先后顺序来确定“第二优先级”的,这并不是真正意义上的优先级别,所以没有必要为其单独抽象出一个IPriorityObject接口来。同时,ISamePriorityObjectManager不要求被管理的对象继承自IPriorityObject接口也是为了扩大其被单独复用的范围。
IPriorityManager接口直接从ISamePriorityObjectManager接口继承,说明IPriorityManager实际上要做工作与ISamePriorityObjectManager是相同的,只不过IPriorityManager管理的对象需要首先按优先级别进行分类,然后再使用ISamePriorityObjectManager管理处于同一优先级别的对象。
接下来我们看PriorityManager的具体实现。
在PriorityManager中,有这样的一个约定:优先级别是用int表示的,其值是从0开始连续的一串整数,整数值越小,表明优先级越高。当Initialize方法被执行后,优先等级的范围就被固定下来。比如PriorityLevelCount值设为4,则PriorityManager所支持的优先等级即为:0,1,2,3。
基于这样的约定,PriorityManager内部使用了一个ISamePriorityObjectManager数组,数组的索引值就对应着优先级别值。比如,数组中index为1的ISamePriorityObjectManager管理器中的所有对象的优先级别值都是1。
有了这两点认识,再看PriorityManager的源码就相当容易了,下面是其中的关键点:
(1)在类似AddWaiter、RemoveWaiter这样的方法实现中,都是先通过其参数对象的PriorityLevel属性定位到对应的ISamePriorityObjectManager管理器,然后再做进一步的处理的。
(2)如果目标对象的PriorityLevel属性值超过了约定的范围,PriorityManager会根据当前的情况做灵活的处理。比如,如果是调用AddWaiter加入一个这样的对象,则会抛出一个“不支持该优先级别”的异常;而如果是在类似RemoveWaiter这样的方法中,则会忽略这个对象。
(3)如果PriorityManager管理器中没有任何对象时,PriorityManager的GetNextWaiter方法直接返回null,而不是default(T),这是因为在PriorityManager定义的泛型约束中,要求T必须是一个引用类型。这就没有了前面提到的SamePriorityObjectManager的GetNextWaiter方法的返回值可能导致的问题。
(4)GetWaitersByPriority方法返回的对象数组具有这样的特征:优先级别越高的对象,其在数组中的位置索引就越小;同一优先级别的对象,加入时间越早的,其在数组中的位置索引越小。
4. 使用时的注意事项
(1) 如果你的系统仅仅需要按照对象的到达顺序来决定先后处理的顺序,那么直接使用ISamePriorityObjectManager就可以满足需求了,没有必要使用IPriorityManager这个更复杂的类。使用ISamePriorityObjectManager还有一个好处就是,被管理的对象不需要实现IPriorityObject接口,这样使用起来会更加方便。
(2) 如果在你的系统中不是使用0,1,2,3…这样的数值来表示优先级别的,那么你可以建立一个转换映射来完成优先级别值到数字的转换。并遵从PriorityManager所要求的约定。
(3) PriorityManager的Initialize方法一旦被调用后,其PriorityLevelCount属性便不应该被修改。或者说,即使该属性在之后被修改,也不会产生任何效果。
5.扩展
优先级管理器PriorityManager暂时没有任何扩展。
注:ESBasic源码可到http://esbasic.codeplex.com/下载。
ESBasic讨论QQ群:37677395
ESBasic开源前言
- ESBasic 可复用的.NET类库(14) -- 优先级管理器 IPriorityManager
- ESBasic 可复用的.NET类库(08) -- 定时任务管理器 TimingTaskManager
- ESBasic 可复用的.NET类库(12) -- 对象管理器 IObjectManager
- ESBasic 可复用的.NET类库(13) -- 分组对象管理器 IGroupingObjectManager
- ESBasic 可复用的.NET类库(16) -- 定时刷新缓存管理器 IRefreshableCacheManager
- ESBasic 可复用的.NET类库(26) -- Round缓存管理器RoundCacheManager
- ESBasic 可复用的.NET类库(10) -- 简易的读写锁 SmartRWLocker
- ESBasic 可复用的.NET类库(01) -- 时刻 ShortTime
- ESBasic 可复用的.NET类库(07) -- 回调定时器ICallbackTimer
- ESBasic 可复用的.NET类库(06) -- 循环任务切换器 CircleTaskSwitcher
- ESBasic 可复用的.NET类库(05) -- 工作者引擎 IWorkerEngine
- ESBasic 可复用的.NET类库(04) -- 循环引擎 ICycleEngine
- ESBasic 可复用的.NET类库(03) -- 圈 Circle
- ESBasic 可复用的.NET类库(02) -- 日期 Date
- ESBasic 可复用的.NET类库(01) -- 时刻 ShortTime
- ESBasic 可复用的.NET类库(00) -- 开源前言
- ESBasic 可复用的.NET类库(11) -- 双向映射 IBidirectionalMapping
- ESBasic 可复用的.NET类库(09) -- 心跳监测器 IHeartBeatChecker
- 把一整段字符的每行的右边的空格去掉的函数
- WinForm 几种获得应用程序启动路径的方法
- 虚拟空间不支持Flv播放
- Install Oracle10gRac On Solaris + HDS + Veritas5.0
- 紧急购买200台以上联想M10电信的上网本
- ESBasic 可复用的.NET类库(14) -- 优先级管理器 IPriorityManager
- makefile基础及常用规则 && MTK安卓平台进入menuconfig
- 装修
- 正则表达式示例
- 关于Inactivity()函数配合活动对象,获取手机不活动时间,实现特定功能
- VB打开文本文件的各种方法
- SELECT和一次的INSERT/DELETE/UPDATE需要用事物管理?
- VCS 5.0 小记
- 未能加载文件或程序集“Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral