分类:
2008-09-09 15:19:34
”,
使它在页面上显示导航条为“系统导航:所有目标>>我进行中的目标>>目标:一起学习Struts(MVC)我的日记列表>>日记:解决:jsp页面中文显示问题”(见图例:/pictures/navigationdesign/disDiaryInMine2.gif )?
解决这个问题我用两步走的方法:
<1>、用一个方法可以把每一个导航节点的封装字符串转换为导航URL字符串。主要是根据需要显示的页面类型和参数ID来进行转换,实现过程比较简单,请查看源文件中负责解封导航节点封装字符串的方法(/WEB-INF/src/com/learndiary/website/util/Pager.java中的public static String decodeNodeStr(String naviStr, String nodeNaviStr, HttpServletRequest request, boolean ifLast) throws Exception );
<2>、把每个节点封装的字符串联接在一起,形成完成的页面层次导航所需的URL字符串。根据惯例,当前(也就是最后一个节点的导航URL灰化,无链接 )。具体实现请查看源文件中负责解封整个导航封装字符串的方法(/WEB-INF/src/com/learndiary/website/util/Pager.java中的public static String decodeNaviStr(String naviStr, HttpServletRequest request) throws Exception );
具体的分析设计过程请见我上面提到的第一篇日记中的“把封装的字符串转化为下一个页面显示导航条所需要的字符串”。
(4)、在页面上显示层次导航的URL字符串
在需要层次导航的页面上,把从request中获得的属性“navigation”显示在页面的左上部,并把从request中获得的相应的封装导航字符串naviStr作为参数附在每一个URL的后面就行了。
至此,学习动态导航系统中的层次导航部分已经设计完成,下面继续进行显示“上一条”和“下一条”的水平导航部分的探索。
2、同一列表中帖子间的导航(即上一条,下一条类似的导航 )的实现过程:
1)列出导航需求列表:
(1)、在所有目标列表中:
1>目标内容;
2>目标的日记列表(上一条:在这里即上一目标的日记列表);
3>日记列表中的日记内容;
(2)、在检索结果页面中:
1>检索目标列表:
<1>目标内容;
<2>目标的日记列表;
<3>日记列表中的日记内容;
2>检索日记列表:
<1>日记内容;
<2>所在目标的日记列表中的日记内容;
(3)、您的进行中的目标列表:
1>目标内容;
2>目标的全部日记列表;
3>目标的我的日记列表;
4>目标的全部日记列表中的日记;
5>目标的我的日记列表中的日记;
2)分析实现上一条、下一条导航所需的参数:
因为是实现同一列表中的同一级别的帖子之间的导航,所以只需要得到需要导航的条目的ID就行了,其它所有参数都不必改变。
3)确定实现上一条、下一条导航的实现方法:
现在的问题是:如何根据当前条目的ID,得到上一条目和下一条目的ID呢?
答案是:条目的列表,条目在列表中的排序方式,当前条目的ID。为了在查询结果集中得到当前条目的前后条目的ID,可以有下面的方法:
(1)在一个直接操纵数据库的方法中从查询结果集中取出每个ID(整型)后,保存在数组中,马上关闭数据库连接,减少数据库连接开销。然后在同个方法中取得前面、当前、后面记录的ID,只返回这3个元素的数组给Pager类(负责产生页面导航所需要的URL字符串的工具类)处理,这样,可以保证每条数据都是最新的,但是要不停的开启和关闭数据库连接;
(2)把查询结果产生的数组全部存在session中,Pager在session中取数据。这样,可以减少数据库的查询,但是存在两个问题,那个比较长的数组在session中始终占用内存,还有,取出的数据的排序关系可能是过期的(这时,有人往数据库中增加或修改了数据)。
我觉得第一种方法可以减轻对网站虚拟主机资源的压力,决定采用第一种方法。
另外,在直接操纵数据库产生的结果集中查询邻近的ID会出现几种结果呢?
这里用“-1”表示没有相应的帖子。
1>在用户查看帖子期间,这篇帖子被删除了,结果返回:{-1,-1,-1};
2>只有一篇符合要求的帖子,结果返回:{-1,当前帖子ID,-1};
3>当前帖子是第一篇帖子,结果返回:{-1,当前帖子ID,下一条帖子ID};
4>当前帖子是最后一篇帖子,结果返回:{上一篇帖子ID,当前帖子ID,-1};
5>当前帖子前后都有帖子,结果返回:{上一篇帖子ID,当前帖子ID,上一篇帖子ID};
现在,就可以把这个含有前一条、当前、后一条帖子ID的整型数组传给Pager类中的产生上一条、下一条导航URL字符串的相应方法进行处理了。
下面是上一条、下一条导航的具体设计。
4)进行上一条、下一条导航的设计:
(1)、根据层次导航的导航字符串确定上一条、下一条导航所在的层次导航位置:
进行上一条、下一条的导航需要知道被导航的帖子所在的层次导航的位置。例如:显示一篇日记的内容:
在所有目标模块中,导航条为:所有目标-》日记列表-》日记内容;(图例:/pictures/navigationdesign/disDiaryInAllGoal.gif )(路径1)
在检索模块中,导航条为:所有目标>>检索>>检索日记列表>>日记内容;(图例:/pictures/navigationdesign/disDiaryInSearch.gif ) (路径2)
在您的目标中,导航条为:所有目标>>我进行中的目标>>本目标我的日记列表>>日记内容;(图例:/pictures/navigationdesign/disDiaryInMine.gif ) (路径3)
在上一条、下一条的导航中,层次导航条除了上一条目和下一条目内容的改变,其余是不会变的;而且,要得到当前条目的前后条目的ID,在不同的层次导航中是不同的。例如:在上面的路径1中,得到日记列表的查询条件是本目标下的所有日记;在路径2中,得到日记列表的查询条件是检索页面的条件组合;在路径3中,得到日记列表的查询条件是本目标下的用户的所有日记。而且,在学习日记的设计中,这三种情况的排序方式是分开的,可以由用户在浏览时自选的。
为了得到不同层次导航下的上一条、下一条URL导航字符串,我在页面导航URL字符串产生工具类Pager((/WEB-INF/src/com/learndiary/website/util/Pager.java)中用了3个重载的、用于产生上一条、下一条导航URL字符串的方法来征对不同的三种情况(与前面说的3种路径不是一一对应的),分别是:
1>、(包括对这一路径下的目标列表中目标的浏览和目标的日记列表的浏览,和检索目标列表中目标的浏览,和检索日记列表中日记的浏览):public static String getPreNextNaviStr( char toPageType, String url, HttpServletRequest request, String naviStr, int currentID, String condition,int orderType, int direction) throws Exception。输入参数是:toPageType-请求的页面类型,url-请求的页面的“/***Action.do”路径,request-请求对象,naviStr-当前页面的导航封装字符串,currentID-当前条目的ID,condition-查询的where子句,orderType-排序类型,direction-排序方向。为了分离数据库访问的代码,在这个方法中调用了一个直接访问数据库的类(/WEB-INF/src/com/learndiary/website/db/PageDB.java)中的方法(public int[] getAdjacentIDs(String tableName, int currentID, String condition, int orderType, int direction) throws Exception )来得到含有前一条、当前、后一条帖子ID的数组;
2>、(包括对检索日记列表的日记所在目标、日记所在目标下的日记列表的浏览):public static String getPreNextNaviStr(String url, HttpServletRequest request, String naviStr, int searchDiaryID, String condition,int orderType, int direction) throws Exception。输入参数是:url-请求的页面的“/***Action.do”路径,request-请求对象,naviStr-当前页面的导航封装字符串,searchDiaryID-搜索日记列表中当前日记的ID,condition-查询的where子句,orderType-排序类型,direction-排序方向。为了分离数据库访问的代码,在这个方法中调用了一个直接访问数据库的类(/WEB-INF/src/com/learndiary/website/db/PageDB.java)中的方法(public int[] getAdjacentIDs(String tableName, int currentID, String condition, int orderType, int direction) throws Exception )来得到含有前一条、当前、后一条帖子ID的数组;
3>、(包括对进行中的目标、退出的目标、完成的目标列表中目标的浏览):public static String getPreNextNaviStr(String url, HttpServletRequest request, String naviStr, int userID, int currentID, int myGoalTypeFlag, int orderType, int direction) throws Exception。输入参数是:url-请求的页面的“/***Action.do”路径,request-请求对象,naviStr-当前页面的导航封装字符串,userID-当前用户ID,currentID-当前条目的ID,myGoalTypeFlag-用户目标的类型(进行、退出、或者完成),orderType-排序类型,direction-排序方向。为了分离数据库访问的代码,在这个方法中调用了一个直接访问数据库的类(/WEB-INF/src/com/learndiary/website/db/PageDB.java)中的方法(public int[] getAdjacentIDs(int userID,int currentID, int myGoalTypeFlag, int orderType, int direction) throws Exception)来得到含有前一条、当前、后一条帖子ID的数组;
现在的问题是怎么样来区别上面1>、2>、3>中列出的各种情况,并调用对应的“getPreNextNaviStr”方法来产生正确的上一条、下一条的URL导航字符串呢?
我的答案是根据当前页面的封装导航字符串来确定,我通过分析上面1>、2>、3>中列出的各种情况的导航字符串的特征码,然后在程序中通过检索特征码来确定当前页面的层次导航位置(对应于上面不同的几种情况),这是一个烦琐的过程,这里仅列举一二:
例如:在检索目标的列表中的目标页面中的封装导航串一定会含有“ae”两个字符,且帖子类型为“目标”;在检索日记的列表中的日记页面中的封装导航串一定会含有“ad”两个字符,且帖子类型为“日记”。
(2)、确定在哪几个Struts的Action中需要处理上一条、下一条导航:
1)、首先,需要导航的地方有三种情况:
1>、目标内容
2>、日记内容
3>、目标的日记列表
2)、
1>、1)的1>和2>需要放在DisGoalContentAction.java(/WEB-INF/src/com/learndiary/website/action/disgoal/DisGoalContentAction.java)中处理;
2>、1)的3>需要分在几个地方处理,分别是:
1>检索目标的日记列表,所有目标的日记列表,进行、完成、退出的日记列表:在DiaryAction.java(/WEB-INF/src/com/learndiary/website/action/disdiary/DiaryAction.java)中处理;
2>进行、完成、退出的我的日记列表:在MyDiaryAction.java(/WEB-INF/src/com/learndiary/website/action/mydiaries/MyDiaryAction.java)中处理;
(3)、在页面上显示上一条、下一条导航的URL字符串;
在需要上一条、下一条导航的页面上,把从request中获得的属性“preNextNavigation”显示在页面的右上部和右下部就行了。
上一条、下一条导航完整的设计分析过程见我上面提到的第二篇日记:“分析学习日记横向导航及开几个窗口的思路”(/disDiaryContentAction.do?searchDiaryID=&goalID=1167&naviStr=a10a2506ah1128 )。
3、父帖与子帖列表的双向导航的实现过程:
1)列出导航需求列表:
(1)“目标<──>目标下的所有日记列表”的双向导航;
(2)“目标<──>目标下我的日记列表”的双向导航;
2)分析实现双向导航所需的参数:
只需要目标的ID或日记列表的目标ID和导航封装字符串;
3)确定实现方法:
直接在Action中得到当前目标的ID或日记列表的目标ID和导航封装字符串,附在“***Action.do?”的后面就行了。
4)进行双向导航的设计:
(1)、确定在哪几个Struts的Action中需要处理双向导航:
1>、在“目标──>目标下的所有日记列表”和“目标──>目标下我的日记列表”的导航,需要放在DisGoalContentAction.java(/WEB-INF/src/com/learndiary/website/action/disgoal/DisGoalContentAction.java)中处理;
2>、在“目标下的所有日记列表──>目标”的导航,需要放在DiaryAction.java(/WEB-INF/src/com/learndiary/website/action/disdiary/DiaryAction.java)中处理;
3>、在“目标下的我的日记列表──>目标”的导航,需要放在在MyDiaryAction.java(/WEB-INF/src/com/learndiary/website/action/mydiaries/MyDiaryAction.java)中处理;
(2)、在页面上显示双向导航的URL字符串:
在需要“目标──>目标下的所有日记列表”和“目标──>目标下我的日记列表”的导航的页面上,把从request中获得的属性“jumpToViewDiaries”显示在页面的右上部和右下部;在“目标下的所有日记列表──>目标”和“目标下的我的日记列表──>目标”的导航中,把从request中获得的属性“jumpToViewGoal”显示在页面的右上部和右下部就行了。
四、总结:
1、心得:
1)、编码前的分析设计是非常重要的,这一步工作做好了,编码就很容易了(但我还做得不够,如下面的第3)条心得。可是,也许是我的水平有限,有些应该放在设计时的工作不到编码的时候就是想不到,还望大家给予指点);
2)、Struts框架把程序的逻辑实现代码和页面显示部分能比较好的分离,有利于功能模块的新增和程序的后期维护;
3)、在进行类和类的方法的设计时(如前面提到的Pager类和PageDB类),没有先进行完整的高层设计,是采用边编码边设计的方式,致使类的设计不够面向对象,给后期的理解和维护会造成困难;
4)、如果这种导航设计思路真的有用,有必要把它进行精心的设计,做成插件的形式,这样可以把它方便的应用于需要这种动态导航的各种java的web程序中;
2、这种动态导航的优点:
1)、能够极大的提高用户的浏览体验,使网站的导航更符合逻辑和人们的思维习惯;
2)、能够无限的进行需要导航的页面的增加和减少,后期的维护代码少量增加就行了;
3、这种动态导航的缺点:
1)、实现过程较为复杂,牵涉的页面和逻辑代码较多,权衡实现的代价和收到的效果,真的值得吗?;
2)、动态导航能被用户的使用习惯接受吗?这是一个未知数;
3)、还有什么缺点呢?暂时还没有想出来,大家帮我们想一想吧。