摘要:在使用Microsoft Visual Studio .NET 2003设计投票系统时,我们希望投票系统能够提供一些扩展功能,比如除了正确地完成投票的各个事务外,还能够将投票结果自动发送给投票发起人或管理员。本文讨论了投票结果自动发送功能的设计、实现,以及在此过程中需要注意的问题
关键字:Microsoft Visual Studio .NET 2003、Borland C++ Builder、Windows服务、投票系统、自动发送
一、 总体分析
首先来看看投票系统的工作场景,以便对设计与实现中的问题进行解答。假设现有一个使用Microsoft Visual Studio .NET 2003开发的基于Web页面的投票子系统,某公司某部门要对年终评优进行一次投票,每个员工在登录该投票子系统以后就可以进行投票。管理员在创建该投票项目时指定投票的标题和内容、可选项目、截止时间以及管理员的邮箱地址。在服务器时间到达管理员指定的投票项目截止时间后,员工将无法通过Web页面进行投票,同时服务器将投票结果以电子邮件的形式发送给管理员。
基于上述的工作场景,我们可以了解到,使用Web应用程序实现投票系统[1]的基本处理逻辑并不困难,但是要实现结果的自动发送功能,就不能只依靠Web应用程序。从投票系统的工作情况来看,处理新投票项目的添加、删除、修改,以及投票、通过页面查看投票结果等功能都可以直接交给B/S模式的Web应用程序来完成,客户端只要向Web服务器提出请求就可以完成相应的处理,在完成客户端的请求后,服务器会将处理结果反馈给客户端;然而投票结果的自动发送功能则不一样,它需要这样一种工作方式:长时间驻留内存,时刻监视着系统时间是否已经到达管理员所指定的投票项目截止时间,如果符合条件,就会自动地把该投票的结果发送到指定的邮箱地址。
显然,Web应用程序无法胜任这样的工作方式,我们选择Windows服务来实现这样的功能。Windows服务可以在计算机启动的时候自动启动,一直驻留后台执行,管理员还可以在必要的时候暂停或者停止Windows服务。MMC的服务管理单元为管理Windows服务提供了一个中心位置。
二、 详细分析与设计
为了描述的方便,在这里我们把设计分为两个部分:内部设计和外部设计。
如果把实现投票结果自动发送功能的Windows服务称为投票结果处理服务器,把用于实现投票系统基本逻辑的Web应用程序称为投票子系统,那么内部设计就是指投票结果处理服务器内部处理逻辑的设计,而外部设计的主要任务是完成对投票结果处理服务器和投票子系统之间的通讯设计。
1、 内部设计
我们应该很清楚地看到投票结果处理服务器内部的工作方式:在服务器启动的时候,会自动地将投票项目的信息读入内存,然后进入循环,每隔一定的时间(比如5秒)对读入的投票项目进行判断,如果投票项目过期,则统计投票结果,并将结果发送出去。
首先要解决的是数据存储问题,也就是数据结构问题。投票项目是以数据库记录的形式保存的,那么在读入内存以后,应该以一种什么样的数据结构来存储呢?解决这一问题需要从服务器的工作方式入手。显然,数据库中的投票项目不止一条,由于管理员在创建投票项目时对投票结果的处理方式有不同的选择(可以选择自动发送结果功能,也可以不选择此功能),这就决定了在多条投票项目中可能只有一部分需要对结果进行发送。此外,每一个投票项目记录有多个字段,比如投票项目的ID号、投票内容、截止时间等,这些字段的值构成了投票结果的实体,是所需发送信息的主要组成部分。我们可以考虑使用结构体数组来存储这些投票项目记录,比如:
typedef struct t_VoteItemInfo {
int id; // 投票项目的ID域
String Title; // 投票项目的标题域
String Name; // 投票项目的名称域
// . . .
TdateTime VoteDeadline; // 投票项目的截止时间域
} VoteItemInfo;
typedef VoteItemInfo VoteItemInfoArray[MAX_SIZE];
然而,我们不能确定某一时刻数据库中的投票项目记录有多少,也不能确定在所有的投票项目记录中,需要对结果进行发送的记录有多少,这就使得我们很难确定上面代码中的MAX_SIZE的值,如果MAX_SIZE的值定义得太小,可能会造成投票项目无法一次性读入内存,必要的时候还需要有专门的换入-换出算法来替换数组中的数据,这样实现起来非常麻烦;如果MAX_SIZE定义得太大,又可能会造成存储空间的浪费,投票结果处理服务器是一个常驻内存的Windows服务,大量空间的浪费会对服务器的性能造成影响。
从上面的分析可以看出,数组并不是存储投票项目记录的最好方法,在此我们引入队列,使用单向队列(也就是单向链表)来存储投票项目记录。C++的STL库为我们定义这样的数据结构提供了很好的机遇,下面的代码很容易地实现了这样的定义:
#include <queue>
using namespace std;
typedef struct t_vi {
int id; // 投票项目的ID