要开发某个项目里某个模块,通常都遵循一个指导,并有一个一般化的流程,在做任何事情之前,有个规划和原则是明智的,无论是核心代码基础设计,还是界面基础设计,以及遍布整个开发生命周期各个阶段的各种约定,规则,这些都是基础。
建立核心代码基础架构是一些细小而相关的任务的集合,它提供一些整个模块都需要基础服务,全局变量配置服务,安全基础服务,数据访问服务,调试跟踪服务,日志记录服务,错误处理服务,以及其它一些通用的服务,之所以把这些功能称为服务是因为你在写以后的业务编码的时候可以以松散耦合的方式来使用这些服务,你在设计基础架构的时候要尽量做到这一点,这使你的架构变的灵活,在以后修改架构的时候不至于过多的牵连到其它层次的代码,并且这些写出的架构有很好的可扩展性,在你的架构不支持某项新业务的时候可以在基础架构里添加一个服务来支持它。架构不易写的过于细致,造成过渡设计,也不应该写的过于简单,没有什么实际作用,不要走这两个极端,关于这方面的争论足可以写一篇长篇大论的文章。在ASP里主要就是写一个通用的函数,放在某个固定的文件夹里,在使用某项架构服务的时候把它包含进去,然后调用里面的函数。在ASP.NET里就是写一个或几个独立的类库,供使用的时候调用。关于建立架构大家可以参考一些其它的书籍,推荐看一下《asp.net电子商务高级编程》里使用的那个基础架构和《.net企业应用高级编程》里的那个架构,它们会给你相当多的指导,这两个架构一个适用于小型的电子商务平台,一个适用于中等规模企业的应用平台,后者使用了很多企业应用技术(比如说remoting,性能计数器等)来支持企业级应用的开发。
界面基础设计,提供一些界面显示的重复元素(导航,脚注等),以及提供软件界面一致性的功能。Web开发里常用css来保持整个站点风格的一致性,然后写一个HTML模板或者输出HTML代码的函数以供多个页的重复调用,在ASP.NET里可以有更多的选择,用户控件,服务器控件等,总之,这些都是一些客户端的技术,需要对xml,xslt,css,javascript,html等技术有个了解。关于如何提高软件的用户体验可以参考其它一些文章,推荐微软出的《windows用户体验》(《windows user experience》)一书。如果你开发的是WinForm程序,建议看看office 2003系列软件的界面和易用性设计,如果你是开发WebForm程序建议看看微软的官方网站,并思考一些东西。
各种约定和规则包含的方面有很多,有编码约定,命名规则,文件夹设置,命名空间约定,以及各种文档约定等。在设计开发之前尽量确定它们,几乎各种语言都有自己的编码和命名约定,比如说在表示常量的时候都用大写字母,私有变量用下划线开头(类c的语言,如果使vb的话,私有变量可以用下划线结束)。也许你公司内部的各种约定和厂商和标准组织提供的约定有所冲突,但是建议还是使用你的开发小组已经习惯了的命名规则,因为强制学习另外一种规则需要时间。尽量保持这些规则在同一个项目的各个模块里遵循一种原则,保持一致性。文件夹设置也有一套规则,比如说Control目录里放置用户控件,Style目录里放置样式表文件,一个项目分配一个子目录等。类库命名空间用 “公司名.产品名.项目名开头”,为每个项目分配有意义的名字空间可以增强程序的可维护性。
开发流程也非常重要,它来保证你在开发中有条不紊并提高开发效率,避免走弯路。在做完需求后,我们按一下流程来完成开发。首先使目标陈述和问题描述,这包括罗列要实现的功能,权限描述,画用例图,写用例描述等。这个过程的结果为后面的工作提供指导,在开发过程中要经常看看你在这个过程写的东西,并在必要的时候修改更新,因为客户的需求常常也会更新增加的。因为我做的一般都是小工程,我们的项目也不可能遵循大型项目开发流程,不必画完整的UML图,也不遵循RUP,XP,FDD里面的过多规则,一切出自于一种经验。
描述完功能后开始设计数据库,设计完数据库后设计界面,设计完界面后根据界面知道来确定需要的编码,接下来编码,最后测试运行程序。我认为一个能让客户满意的程序最少要满足以下几点:完成主要的功能;安全;界面友好,易用性强;这也是衡量一个软件是否成功的最低标准。
这一切是一个简化的过程,我无法把他写的过于细致,也没有必要,每个人都有自己的开发习惯,而且这个流程和指导也会随着个人的经验而改动更新。
下面是我写的一个OA里的一个模块的开发过程,我试图使用我写的以上这些经验。
工作计划搜索模块
问题陈述
1. 按部门查看工作计划,指定部门名称;
2. 按人员查看工作计划,指定人员名称;
3. 查看日工作计划,指定起始日和终止日;
4. 查看周工作计划,指定起始周和终止周;
5. 查看月工作计划,指定起始月和终止月;
6. 查看年工作计划,指定起始年和终止年;
权限描述:
1. 管理员,总经理,董事长可以查看所有部门员工已提交的工作计划;
2. 各部门经理,主管可以查看本部门所有员工已提交的工作计划;
3. 每个员工可以查看自己的工作计划,包括已提交和未提交的;
角色设置:
1. 管理员
2. 董事长
3. 总经理
4. 部门经理
5. 员工
部门设置:
1. 市场部
2. 技术部
工作计划表【log】
【ID】 编号
【UserName】 添加用户
【Title】 标题
【Content】 内容
【AddDay】 填写时间
【Dept】 添加用户所在部门
【Class】工作计划类型,day,week,moth,year分表表示日计划,周计划,月计划,年计划
【State】计划状态,0为默认值,表示计划未提交,1表示计划已提交
工作日志搜索流程
图1 权限判断流程
图2 选择日志类型
设计界面
在设计好数据库,并确定了一定的业务流程后就可以着手设计用户界面了。用户界面不仅提供一个用户入口,还为你设计后台编码提供了知道,界面设计出来之后基本上就可以确定后台要编写哪些代码来相应这些界面了。为了提供一致的用户体验,我们可以用html+css+javascript来没让windows程序的效果,选择了不同的日志类型后时间范围会有所变化,这样习惯使用windows的用户在使用软件的时候不至于困惑。
按日查看
按周查看
按月查看
按年查看
下面是生成界面的主要代码
<!--#include file="../inc/conn.asp" -->
<!--#include file="../inc/const.asp" -->
<!--#include file="../inc/pubfunction.asp" -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>查找日志</title>
<link href="../style/wawa.css" rel="stylesheet" type="text/css"><!-- 链接到一个整体的CSS -->
<script language="JavaScript"src="../script/calendar.js"></script><!-- 提供日历功能,梅花雨写的 -->
<script language="JavaScript">
<!--
WebCalendar.yearFall = 50; //定义年下拉框的年差值
WebCalendar.format = "yyyy-mm-dd"; //回传日期的格式
WebCalendar.timeShow = false //是否返回时间
WebCalendar.drag = true //是否允许拖动
WebCalendar.darkColor = "#0000D0"; //控件的暗色
WebCalendar.lightColor = "#FFFFFF"; //控件的亮色
WebCalendar.btnBgColor = "#FFFFF5"; //控件的按钮背景色
WebCalendar.wordColor = "#000080"; //控件的文字颜色
WebCalendar.wordDark = "#DCDCDC"; //控件的暗文字颜色
WebCalendar.dayBgColor = "#E6E6FA"; //日期数字背景色
WebCalendar.todayColor = "#FF0000"; //今天在日历上的标示背景色
WebCalendar.DarkBorder = "#D4D0C8"; //日期显示的立体表达色
//-->
</script>
<style type="text/css">
<!--
.dispaly_none{display:none;}
.dispaly_block{display:block;}
-->
</style>
<script language="JavaScript" type="text/JavaScript">
//改变某个div的css样式,以控制显示还是隐藏
function ChangeClass(div_id)
{
var div_array = new Array(div_day,div_week,div_month,div_year);
var t_div
for (t_div in div_array)
{
if(div_id.id == div_array[t_div].id)
{
div_array[t_div].className = "dispaly_block";
}else
{
div_array[t_div].className = "dispaly_none";
}
}
}
</script>
</head>
<body>
<%
dim i,sql
ShowSearch()
%>
<%Sub ShowSearch%>
<form name="form1" method="get" action="SearchOk.asp">
<table width="500" border="0" align="center" class="InputFrameMain">
<tr class="Listrow1">
<td colspan="2" class="ListCellRow" ><div align="center">选择查询条件</div></td>
</tr>
<tr class="InputFrameLine" >
<td width="23%" ><fieldset>
<legend><b>选择日志类型</b></legend><table width="100%" height="100" border="0">
<tr>
<td><input name="logtype" type="radio" onclick="ChangeClass(div_day)" value="day" checked>
日计划</td>
</tr>
<tr>
<td><input type="radio" name="logtype" value="week" onclick="ChangeClass(div_week)">
周计划</td>
</tr>
<tr>
<td><input type="radio" name="logtype" value="month" onclick="ChangeClass(div_month)">
月计划</td>
</tr>
<tr>
<td><input type="radio" name="logtype" value="year" onclick="ChangeClass(div_year)">
年计划</td>
</tr>
</table>
</fieldset></td>
<td width="77%" ><fieldset>
<legend><b>选择时间范围类型</b></legend>
<table width="100%" height="100" border="0">
<tr>
<td colspan="2" valign="top">
<div id="div_day" class="dispaly_block">开始日期
<input name="Day_StartDay" type="text" class="input" id="Day_StartDay" value="<%=formatdate(now(),"YYYYMMDD","-")%>" size="10" maxlength="10" onfocus="calendar()">
终止日期
<input name="Day_EndDay" type="text" class="input" id="Day_EndDay" value="<%=formatdate(now(),"YYYYMMDD","-")%>" size="10" maxlength="10" onfocus="calendar()">
(YYYY-MM-DD) </div>
<div class="dispaly_none" id="div_week" >开始周
<select name="Week_StartYear" id="Week_StartYear">
<%
For i = 2000 To 2010
Response.Write("<option value="""&i&"""")
If DatePart("yyyy", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
<select name="Week_StartWeek" id="Week_StartWeek">
<%
For i = 1 To 53
Response.Write("<option value="""&i&"""")
If DatePart("ww", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
终止周
<select name="Week_EndYear" id="Week_EndYear">
<%
For i = 2000 To 2010
Response.Write("<option value="""&i&"""")
If DatePart("yyyy", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
<select name="Week_EndWeek" id="Week_EndWeek">
<%
For i = 1 To 53
Response.Write("<option value="""&i&"""")
If DatePart("ww", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
</div>
<div id="div_month" class="dispaly_none">开始月
<select name="Month_StartYear" id="Month_StartYear">
<%
For i = 2000 To 2010
Response.Write("<option value="""&i&"""")
If DatePart("yyyy", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
<select name="Month_StartMonth" id="Month_StartMonth">
<%
For i = 1 To 12
Response.Write("<option value="""&i&"""")
If DatePart("m", Now()) = cint(i) Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
终止月
<select name="Month_EndYear" id="Month_EndYear">
<%
For i = 2000 To 2010
Response.Write("<option value="""&i&"""")
If DatePart("yyyy", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
<select name="Month_EndMonth" id="Month_EndMonth">
<%
For i = 1 To 12
Response.Write("<option value="""&i&"""")
If DatePart("m", Now()) = cint(i) Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
</div>
<div id="div_year" class="dispaly_none">开始年
<select name="Year_StartYear" id="Year_StartYear">
<%
For i = 2000 To 2010
Response.Write("<option value="""&i&"""")
If DatePart("yyyy", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
终止年
<select name="Year_EndYear" id="Year_EndYear">
<%
For i = 2000 To 2010
Response.Write("<option value="""&i&"""")
If DatePart("yyyy", Now()) = i Then Response.Write("selected")
Response.Write(">"&i&"</option>"&vbcrlf)
Next
%>
</select>
</div>
</td>
</tr>
<tr>
<td colspan="2" valign="top"><legend><b>选择时查看类型</b></legend>
<table width="100%" border="0">
<tr>
<td width="27%" valign="top"><input type="radio" name="ViewType" value="dept">
按部门查看</td>
<td width="73%" valign="top"><span class="InputAreaCell">
<select name="Dept" id="Dept">
<%
Set rs = Conn.Execute("select deptid,deptname from dept order by deptid")
do while not rs.eof
%>
<option value="<%=rs(0)%>"><%=rs(1)%></option>
<%
rs.movenext
loop
%>
</select>
</span></td>
</tr>
<tr>
<td valign="top"><input name="viewtype" type="radio" value="username">
按员工查看</td>
<td valign="top"><span class="InputAreaCell">
<select name="UserName" id="UserName">
<%
Set rs = Conn.Execute("select username from users order by username")
do while not rs.eof
%>
<option value="<%=rs(0)%>"><%=rs(0)%></option>
<%
rs.movenext
loop
%>
</select>
</span></td>
</tr>
</table></td>
</tr>
</table>
</fieldset></td>
</tr>
<tr class="InputFrameLine" >
<td ><div align="right">
<input name="Submit" type="submit" class="button" value="提交">
</div></td>
<td ><input name="Submit" type="reset" class="button" value="重置"></td>
</tr>
</table>
</form>
<%End Sub%>
<!--#include file="../bottom.asp" -->
开始编码
正式编码前要确定编码约定,命名规则,以增强编码的可读性,方便别人查看代码,也方便日后维护,如果用结构化编码,需要考虑各个函数的粒度,函数要尽量写的小巧而独立,尽量避免写功能强大,代码繁杂的函数,在面向对象编码里的方法也要遵循这个原则。首先要根据上面画的流程图来分解任务,确定每个独立的用例或者功能,每个独立的功能写个小函数,最后用一个总函数来组织调用这些小函数以完成特定的任务。我没有画类图,只是简单罗列一下,这通常比较简单也很有效。
|
SearchLogByDay() |
按日搜索 |
|
SearchLogByWeek() |
按周搜索 |
|
SearchLogByMonth() |
按月搜索 |
|
SearchLogByYear() |
按月搜索 |
|
GetViewLogFlag(Role) |
'获取某个角色查看日志计划的权限,
'返回值1表示只能查看自己的日志计划
'返回值2表示只能查看本部门员工的日志计划
'返回值3表示能查看任何部门及任何人的日志计划 |
|
FillSelectDept() |
填充部门下拉列表 |
|
FillSelectUser() |
填充用户下拉列表 |
|
SearchLog() |
日志搜索函数,主函数入口 |
|
GetDept(UserName) |
返回某个用户的部门 |
|
chks(Flag,PageFlag) |
返回一个数字是否在一个用逗号分隔开的字符串里,因为一个许日志查看许可对应着若干个角色,所以要判断某个角色是否有某种的日志查看授权 |
|
GetDeptName(DeptID) |
根据部门ID返回部门名称 |
|
|
|
设计一个优秀出色的函数并不容易,你必须小心翼翼的处理函数的不便量,前置条件,后置条件,比如说你要的函数需要一个大于0的参数,但是传递进来的参数是负数,或者你的函数需要返回一个不会空的值,但是最后经过计算得到的值为空,遇到这样的情况你都得考虑去如何处理,你应该尽力的考虑周全,因为你处理不好的话,你的函数很可能给调用者返回一个错误的值,这样会影响别人的工作。你还得处理好你的函数执行过程中可能出现的错误和异常,常规错误的话可以用返回码的形式通知调用者出了什么情况,而出现异常的话,你要尽量保护现场,并尽量不要改变整个系统的状态,尽量报告多的信息给调用者,以便他们决定下一步该如何做。尽量避免你已经修改了某个表的数据后函数出现异常并且无法回滚,你应该用事务确保你的函数里的实际操作(数据库操作或者文件操作等)要么全部执行完毕,要么什么操作都没执行,不能执行了半截由于出错停到了那里。
设计一个好的函数还有好多其它的指导和技巧,不过暂时只能介绍到这里,这些基础的东西要常看常练习才能体会。判断一个函数质量也还有好多其它的标准,比如说代码是否紧凑,注释是否合理,可修改性,强壮性等。