3.1 系统的业务流程
根据用户需求,本系统业务流程的设计如图3-1。
3.2系统的分层设计
在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层、表示层。本系统也采用了这种分层的方式。下图3-2为本系统分层架构图。
图3-2 系统架构图
数据访问层:主要负责数据库的访问。简单的说就是实现对数据表的Select,Insert,Update, Delete的操作。在线考试考试系统的数据访问层中使用了NHbiernate作为OR映射工具。
业务逻辑层:是整个系统的核心,它与这个系统的业务(领域)有关。在线考试的业务逻辑主要是关于考试规则的逻辑。比如考试迟到不能参加考试、不能提前交卷等。如果涉及到数据库的访问,则调用数据访问层。
表示层:是系统的UI部分,负责使用者与整个系统的交互。在这一层中,理想的状态是不应包括系统的业务逻辑。表示层中的逻辑代码,仅与界面元素有关。在线考试中,是利用Asp.net来设计的,因此包含了许多Web控件和相关逻辑。
3.3系统的包的设计
包在.net中对应的术语是命名空间。在线考试系统划分了三个大的命名空间,分别是Web(包括表示层的内容)、BLL(包括业务逻辑层的内容)和DAL包括了数据访问层的内容。其中BLL包括了5个子命名空间,分别是Domain、 DataInterface、Service、Utils和Exception。系统还使用了其他的外部类库,包括Ajax.net,Jquery,NHibernate,Log4Net等。图3-3是系统包图,展示了包之间的依赖关系。另外两个是工具类和自定义异常包,由于与核心设计关系不大图上没有说明。
图3-3 系统包图
图3-4 解决方案图
Domain包含了所有的领域类和映射文件,比如Student、Question等。大部分为数据类,没有什么行为。如图3-5所示
图3-5 领域包的文件组织图
DataInterface包含了数据访问层的接口IDao、IDaoFactiory工厂接口和DaoManage。DaoManage是用来注入具体Dao工厂的简单工厂类,该类同时是访问Dao对象的门面类。如图3-6
图3-6 DataInterface包的文件组织图
Service 包含的是对一个对业务层的简单封装的外观类,Service类本身没有任何业务逻辑。它总是将数据访问的职责委托给Dao对象。将业务逻辑委托给Domain对象。如图3-7。
图3-7 Service包的文件组织图
DAL包含了具体的数据Dao实现类和使用到的几个NHiernate帮助类。该包的Dao对象利用NHibernate实现了ExaminationSystem.BLL.DataInterface中定义的数据访问接口和Dao工厂接口.如图3-8所示。
图3-8 DAL包文件组织图
Web包含了表示层用到的Web页面和js脚本、WebService、Css、xslt等内容。还有一些UI相关类和HttpModule 类。NhibernateSessionModule用来实现Open Session in View。SecurityModule用来实现安全检查。
3.4 各层之间的依赖关系
由于BLL层是整个系统的核心部分,所以表示层和数据访问层都应该依赖与BLL层。就是说要实现数据访问层对业务层的依赖倒置,这需要使用分离接口来实现。如图3-9所示,左图为业务层依赖数据访问层。右图为分离接口后倒置的依赖关系。
图3-9 各层依赖图
业务层和表示层的关系和以前的一样这里就不多介绍了。
3.5 抽象工厂和反射实现对数据访问层的依赖注入
业务逻辑层是不依赖数据访问层的,但是业务层还得使用数据访问层来操作数据。既要使用又不能依赖,这就引出了抽象工厂模式。因为不能new关键字来创建数据访问层的具体Dao对象,所以我们只能从工厂里获得Dao对象,然后通过IDao接口来使用数据访问层的Dao对象。下面给出具体的类图来说明.如图3-10所示
图3-10 抽象工厂类图
为了简单,该图的IDao接口只有一个数据操作方法GetById当然还可以有Save,Update,Delete等方法。IDaoFactory里也只有一个工厂方法GetDao具体实践时候应该是每一个领域类都有一个Dao对象与之对应。比如IStudentDao、ITeacherDao等。而IDaoFactory应该对应的工厂方法可能有GetStudentDao、GetTeacherDao等等。可以看到各个类的依赖关系不存在从业务层到数据层的依赖。当我们想要获得一个具体Dao对象时候,我们先从DaoMangager那里通过反射获得具体DaoFactory的实例。这样我们就可以通过DaoFactory来获得具体的Dao对象了。将依赖关系倒置后我们就可以动态的切换数据访问层。因为我们的业务层不依赖与具体的数据访问层,而是依赖与数据访问层接口。
3.6 各层之间的交互
上面介绍了各层之间的静态关系,下面介绍各层之间是如何动态交互的。用一个实例来说明,比如要显示一个领域对象信息。如图3-11所示。
图3-11 各层交互顺序图
从图上可以看出消息是如何在各层之间传递的首先从显示信息的。showDomainPage发送请求给DomainService服务对象,请求获得id号为1的Domain对象。DomainService从DaoMangager那里获取具体daoFactory对象。DaoManager是根据配置文件动态的加载数据访问层程序集,然后利用反射创建daoFacotry对象的。有了daoFactory,DomainService对象再从daoFactory的工厂方法里获得自己需要的dao对象。最后通过dao对象得到最终需要的id=1的Domain对象。并返回给Page对象。Page对象最后在将Domain对象的内容绑定到页面上显示给用户。这里只是简单的展示了请求是如何在各层之间传递的。当然实际的请求可能更加的复杂。关于如何删除,添加,修改等操作和显示操作的交互方式是类似的这里就不多介绍了。
3.7系统的领域模型
上面介绍了系统各层之间的静态和动态的关系。下面介绍下和领域相关的一些内容。主要就是在线考试系统的领域概念。下图3-12是在线考试系统的领域模型。
图3-12 领域模型图
下面对各个领域概念一一说明
1.Subject代表科目。
2.Chapter代表章节或者知识点。
3.QuestionContent代表了题库中的一道题。
4.TianKongContent,DanXuanContent是QuestionContent的子类分别代表填空题,单选题等。
5.Question代表的是学生考卷上的题。注意是和QuestionContent是有区别的。Question包含学生答案和分值,QuestionContent只是包含一道题的基本信息。Question会引用QuestionContent这样可以减小数据冗余。
6.QuestionContainer代表的是一个大题它可以包含多个Question。比如单选题。
7.Paper表示一张试卷。试卷可以包含多个QuestionContainer。
8.Student表示学生。
9.Class表示班级。
10.Examination表示一次考试。每次考试对应一个考试策略(PaperStrategy)。
12.PaperStrategy是一个用来随机生成试卷的模板。
13.StrategyContainer是用来随机生成大题(QuestionContainer)的模板。
14.StrategyItem策略项:策略项包括一个试题的集合。出题的题数(题数必需小于等于试题集合的数量),分值。用来随机的从试题集合中抽取若干试题并给试题附上指定分值!每个StrategyContainer都包含多个策略项。不同的策略项区分同一题型,不同分值的题目。
上面的领域模型中没有提到Admin, 和Teacher 类。因为这两个领域类与核心模型关系不大为了简洁没有将这两个列出。
3.8系统的数据库设计
数据库的设计和领域模型基本上一一对象。即一个领域类对应一个表。但是处于同一继承结构的领域类只有一个表与之对应。这种方法虽然浪费了些数据库存储空间但是使用起来比较方便。另外多了几个表是用来存储对象之间的关联的关联表。QuestionContent表存储了QuestionContent类的所有子类。Student表则存储了User的所有子类。为了方便Student,Teacher,Admin都继承自User。表StrategyItemQuestionContent,和ExaminationClass则是两个关联表。下图3-13是在线考试系统的ER图
图3-13 数据库ER图