接上篇《机房收费系统重构(一)》
二、概要设计
完成了用例图,并用结合用例图完善了一下需求分析说明书,忘记是第几次修订需求分析说明书。有了用例图,很自然就进入了概要设计阶段。我认为这一阶段就是结合包图解决系统的基本架构。在这里采用了三层架构(符合高内聚,低耦合的思想),并结合了一些设计模式。
下面看我的包图:
可以看到,这个包图,是从最经典的三层UI-BLL-DAL加入设计模式演化而来。
之所以采用抽象工厂模式是考虑到更换数据库的方便。
而应用外观模式,是为了解决UI层和BLL层耦合性过高的问题,UI层不必知道BLL层的存在,Facade(外观)知道BLL层的哪些类负责处理哪些请求,它将UI的请求代理给适当的BLL层的类。使外部调用更方便。
三、抽象各层的类
当我们确定了整个系统的架构,接下来要做的就是细化,这是一个从宏观到微观的过程。
1、抽象实体类
我认为,第一步要做的就是抽象实体层的类(Entity),因为信息系统是对数据的操作和处理,首先必须要有数据,这个时候,我们要返回需求,了解用户的数据要求,以此为依据进行数据库设计,数据库设计参见我的文章:
《数据库设计第三范式》
还有一篇转载《数据库设计经验谈》
数据库设计好了,我们要根据数据库中的表抽象实体类,在机房收费系统中,实体类基本上是跟表一一对应的,一个表映射出一个实体类,表的字段即为实体类的属性。
实体层并不属于三层中的任何一层,它是独立出来的一层,可以把他看做自定义变量的组合,供三层使用。
下面看看我抽象出的实体类:
见图:
从图中,看到有一个EN_PublicField类,这个类并不是由数据表映射而来,它是一个装载公共变量的类,在系统中,尽量不要在类之外建立变量.有全局用的东西,我们可以建一个实体类,把该全局变量作为它的属性。所以,实体类的数量可能多余表的数量.
2、数据访问层
搞定了实体层,再来看看数据访问层(DAL),这一层的主要任务是直接操作数据库,完成对数据的增删改查等。这里我们仍然根据数据表来抽象DAL层的类,基本上也是一个表对应一个类,这样当我们增加新的表,直接增加新的DAL层类就可以,很好地符合了“开闭原则”。
另外,因为DAL层的类是直接对数据库进行操作的类,所以这个类里封转大都有四种方法:增删改查。但根据实际情况会有不同的参数,不同的返回值。
这里加了一层接口,利用反射和抽象工厂,以防更换数据库。见下面的部分截图。
接口:
注:我们看到有一个接口叫ITime,这个接口是用来获取服务器时间,所以DAL层类的数量也可能多余表的数量。
DAL实现IDAL接口
抽象工厂模式+反射+配置文件
3、封装业务逻辑,构成BLL层的类
完成了数据访问层,我们算是打好了地基了,下面我们再看用例图,基本上一个用例封装了一个功能。BLL层的类,我们可以根据功能来分,把与该功能相关的操作集成到一个BLL层的类里,这里我们要把握好粒度,平衡就好。
尽量做到符合单一职责原则,一个类完成一个功能,即不要在BLL层出现类之间互相调用的情况,虽然可以减少代码量,但会增加系统的复杂性,造成模块与模块之间的强耦合。
举个例子:“结账”的时候我们需要查询充值记录表,“查询充值记录”的时候我们也需要查询充值记录表,我们应该在BLL层的结账类里设置查询方法,在查询充值记录类里也应设置查询方法。
如果情况比较特殊,需要交叉调用,例如一个操作需要不断的重复(超过三次),我们一般将它提取出来,供外部调用。这时候,我们应该把调用上移,即,不要在BLL类中调用BLL层类的方法,而是在BLL层的上一层,我们这里用到了外观模式,所以在BLL的上一层即Facade层(见上面的包图)完成对两个类的方法的调用。
举个例子:"上机"需要检查卡号是否存在,"下机"也需要检查卡号是否存在,"充值","退卡","注册卡"都需要检查。这时候我们可以把检查卡号这个操作提取出来。当我们调用的时候,不是在BLL层中直接调用,而是把它提升到Facade层来调用,即,调用上机之前,先调用"检查卡是否存在"。
下面看看我的BLL层
在BLL层用到了策略模式:
4、降低UI和BLL的耦合,采用外观模式,加入外观(Facade)类
BLL层中有很多,很小的类,这就给UI层的调用带来了困难,外观类这一层为UI层提供了一个简单的接口,大大降低了UI层和BLL层的耦合度,也可以看做是一次粒度上的粗化。
见图:
5、界面层(UI)
界面层的类,就是我们的窗体类,有多少个窗体,UI层就有多少个类。
见图:
三、设计模式
上面提到的设计模式有:抽象工厂模式,外观模式,策略模式。在BLL层考虑使用模板方法模式。在UI层,将使用观察者模式,实现状态栏的动态变化。如果使用外部报表控件,很可能用到适配器模式。
总结:上述设计只是个人观点,有待在编码过程中验证,欢迎拍砖。