背单词软件-设计与实现
2014-08-28
源代码下载:Code-ReciteWord.zip
三层结构
数据访问层
业务逻辑层
表现层
数据结构
永久层
业务逻辑层
难点与解决方案
Recite的自动播放功能
Recite的播放暂停功能
窗口之间传递数据
设置信息处理
经验与教训
永久层数据结构的问题
待研究
三层结构
返回
三层结构优点:职责明确,易于理解,提高代码重用率,便于改动。
三层结构缺点:上层依赖下层,下层修改,上层也得相应修改。
图1 Solution
从图1可知,软件是三层结构,分别是表现层、业务逻辑层、数据访问层。三层结构都是上层调用下层:表现层->业务逻辑层->数据访问层,表现层->数据访问层亦可。
数据访问层
数据访问层提供对永久层数据(比如:文件,数据库)的简单操作(增删改查)。
注意:这一层操作逻辑一定要简单,便于业务逻辑层调用。
图2是TaskDataAccess的方法和属性,只有对xml文件增删改查、创建、保存操作
图2 TaskDataAccess
业务逻辑层
如果数据访问层方法的比较简单,那么数据访问层的方法相对复杂,粒度更大,加入更多判断和相关操作。
下面是TaskDataControl.cs的主要的公共方法:
表现层
表现层包括界面设计以及状态。以frmReciteWord_Task为例,它包括界面frmReciteWord_Task、幕后代码,还有TaskDiplay.cs。其中,TaskDisplay.cs主要负责界面状态,把它分离出来,主要是为了方便理解,和重用。
TaskDisplay.cs通过frmReciteWord_Task.cs通过参数传入的控件引用来控制控件状态。(这可能不是很好的实现-待定)
数据结构
返回
永久层
文件TaskFile.xml
我们可以看到,永久层的属性有:Name, UnitStatus, Period, PeriodStatus, CurrentReciteTime
业务逻辑层
文件TaskDataControl.cs
图3 Task Business data constructor
业务逻辑层属性有:Name, UnitStatus, Period, PeriodStatus, CurrentReciteTime, Priority, NextReciteTime。其中,Priority, NextReciteTime是计算出来的。
在这些属性中,UnitStatus和PeriodStatus比较相似,他们的值,我用枚举一一列出来了:
我们可以看到两个状态中都有Start和Complete。因为要把课文中单词永久记住,需有多个周期的记忆、遗忘。UnitStatus的Start和Complet指的是开始学习,到最后结束;而PeriodStatus的Start和Complete只当前周期的开始结束。
注意:数据结构中,对某个属性理解比较模糊时,一定要停下来思考清楚。清晰的设计,带来轻松的实现。
当然,永久层的数据结构和业务逻辑层的数据结构不必完全一一对应,视实际情况而定。
难点与解决方案
返回
Recite的自动播放功能
当时想用线程实现,然后在线程中直接更改winForm中控件的值来更改winForm的界面显示。但这样做会抛出异常:
Cross-thread operation not valid: Control accessed from a thread other than the trhead it was created on.
解决方案是通过control.invoke来更改控件的值。具体原理 横刀天笑 写的博客 WinForm二三事:
WinForm二三事(一)消息循环
WinForm二三事(一)补遗
WinForm二三事(二)异步操作
WinForm二三事(三)Control.Invoke&Control.BeginInvoke
Recite的播放暂停功能
上面提到自动播放是通过thread实现的,原本暂停功能想通过thread自带方法suspend()实现,但高版本net framework mark这个方法depreciated。因此,另找出路,用AutoResetEvent类来通知thread wait or contiune。看代码示例:
窗口之间传递数据
这里主要通过两种方法:
- 通过公开的属性
- 单独设个类,通过静态set和get方法
设置信息处理
设置信息见上一篇设置功能,一般可以存放在xml配置文件中。但为了程序更简单,把这些信息放在注册表中。set,get注册表的静态方法都放在Utility.cs文件中。
经验于教训
返回
永久层数据结构的问题
由于Unit的Priority的确定跟UnitStatus,PeriodStatus,NextReciteTime相关,见上一篇任务管理功能表三 Priority的取值。而且,有这样的设计,当CurrentReciteTime的PeriodStatus为‘Complete’,NextReciteTime的背诵时间已到,会自动把CurrentReciteTime改为NextReciteTime,PeriodStatus改为‘Start’,NextReciteTime的值在根据CurrentReciteTime+EbbinghausPeriod确定。因此觉得NextRecitTime应该放在永久层中。
但在调整Ebbinghaus Period的时候,发现已确定下来的NextReciteTime很难修改。于是,想到是数据结构出现问题。NextReciteTime可以根据CurrentReciteTime+EbbinghausPeriod确定,因此,只需要保存CurrentReciteTime就可以了。
心得:
- 清晰地设计带来简单的实现。
- 代码越写越明,如果发现难以进行,多半是原来设计逻辑有问题。
待研究
Winform程序如果不在幕后代码partial文件中出现的异常,比如在TaskDataAccess.cs中出现outOfIndex,null object reference异常,不会在界面上显示异常。
参考:
[1] 三层结构_百度百科
文章知识点与官方知识档案匹配,可进一步学习相关知识C技能树首页概览113456 人正在系统学习中 相关资源:下拉通刷词软件v3.1.zip-其它代码类资源-CSDN文库
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!