首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

纯 servlet:重新考虑视图(2)

纯 servlet:重新考虑视图(2)

表单和它的处理器现在该看看代码了。在锦标赛的第一轮开始之前,用户进入 Picks 页面(见图 2),选择他们喜欢的球队。在此之后,他们可以通过只读输出的形式查看自己和其他玩家的选择情况。
图 2. Picks 页面在生成这个页面时,Picks servlet 做的第一件事情是从业务层获取它的用户对象(在这个系统中,是 Player),并执行一项安全检查:
1
2
3
4
5
6
7
8
9
10
PlayerManager playerMan = PlayerManager.GetInstance();
Player player = playerMan.select(session.getAttribute(P_PLAYER_ID), true);
boolean readOnly = GetCutoffDateIsPassed() && !player.getAdmin();
String playerID = request.getParameter(P_PLAYER_ID);
if (playerID != null)
  if (readOnly || player.getAdmin())
    player = playerMan.select(playerID, true);
  else
    throw new ServletException("You may not view other players' picks"
          " until the cutoff date has passed:  " + CutoffDate + ".");




这确保正常用户根据业务规则查看或编辑选择的球队。它还建立一些局部变量,这些变量将决定页面的表现,尤其是 readOnly。接下来,建立一个 Team 对象数组,每个对象代表一支参赛球队。然后,调用一个方法,从数组生成按字母表排序的 map,下拉控件需要用到这个 map:
1
2
3
TeamManager teamMan = TeamManager.GetInstance();
Team[] teams = teamMan.selectAll();
Map selectTeams = getDropDownMap(teams);




现在,开始输出:
1
out.printPreContent(null, out.SCRIPTFile("/js/picks.js"));




这个方法输出页面的第一部分,包括完整的 HEAD 标记、BODY 开始标记和页面顶部的徽标。注意指向一个 JavaScript 文件的 URL,它添加在 HEAD 中。您可能会认为,在部署 WAR 文件时这种方法会失效,因为它将在 URL 的开头添加上下文前缀 /madness。实际上,上下文前缀是动态地传递给 MadnessWriter 构造函数的,然后构造函数自动地将它加在任何 URL 的开头,并加上斜线;如果您的上下文是不确定的,那么这个特性就非常有用。
下一个调用输出主菜单:
1
out.printMenu(URL_PICKS);




通过传递要显示的页面的 URL,让 MadnessWriter 实例跳过这个页面的链接(也可以禁用它)。然后调用一个方法,开始输出 TABLE 元素,我将这个元素称为
1
out.printBeginBox();




这会开始几个标记,直到框包含的具体内容为止。(后面将通过一个相似的调用结束这些标记。注意,上面的 printMenu() 调用了同样的方法。)这种封装方式可以大大简化调试。例如,我曾经遇到一个 bug,框中的某些边界 TD 的宽度是 1%,对于浏览器窗口来说,这个宽度太大了。我将它改为 0%,从而在一个地方进行修改就纠正了整个站点上的效果。这可以用定制的标记库来完成,但是没这么容易。
下面几行输出一个或两个 DIV 元素,第一个在提交表单之后向用户表示成功:
1
2
3
4
if ("true".equals(request.getAttribute(P_SUCCESS)))
  out.printDIV("smallHeading", "Team picks were saved successfully.");
out.printDIV("reminder", "(Reminder:  \"Pick 20\" represents the team you"
      + " think likeliest to win.  \"Pick 1\" is the least likely.)");




"smallHeading" 和 "reminder" 自变量指定要应用于 DIV 开始标记的层叠样式表(CSS)类名,第二个自变量是在 DIV 标记之前输出的文本。如果 reminder DIV 的内容比较复杂,我会调用 out.printBeginDIV("reminder"),这个方法只输出 DIV 开始标记。HTMLWriter 和 HTMLFlexiWriter 中也使用同样的命名模式。但是,HTMLConstants 中的字符串常量不太一样,例如默认的 DIV 开始和结束标记分别使用 DIV 和 END_DIV。
在 reminder 后面,输出一个表单,其中提供下拉控件让用户选择 20 支球队。如果用户只能查看已经做出的选择,那么只输出球队的名称。按照 Java 语法,这个逻辑的表达非常自然:
1
2
3
4
5
6
7
if (!readOnly)
  out.printSELECT(P_PICK + i, selectTeams, teamID);
else
  {
  String teamName = (String)(selectTeams.get(teamID));
  out.print((teamName != null) ? teamName : "(no pick)");
  }




printSELECT() 方法为 map 中的每个键/值对创建一个 OPTION,它预先选择键与 teamID 匹配的对象。
为了完成表单,需要输出显示在页面右边的球队列表。球队的数组按照 NCAA 地区和排名进行排序。每个地区有一个小标题,整个列表显示为两列。这需要一些数学计算,所以将它放在一个单独的方法中,见清单 1:
清单 1. 将输出代码放在一个方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void doRegionList(Team[] teams, MadnessWriter out) throws IOException
  {
  out.print(TABLE + TR);
  out.printBeginTD(null, "regionList");
  for (int i = 0; i < teams.length; i++)
    {
    if ((i & 15) == 0)
      {
      if (i == 32)
        {
        out.print(END_TD + NL);
        out.printBeginTD(null, "regionList");
        }
      out.print(NL + DIV);
      out.print(REGION_NAMES[i >> 4]);
      out.print(":" + END_DIV + OL);
      }
    out.print(NL + LI);
    out.printHTMLEscape(teams.getFullName());
    out.print(" (");
    out.print((teams.getRank() & 15) + 1);
    out.print(")");
    out.print(END_LI);
    if ((i % 16) == 15)
      out.print(END_OL);
    }
  out.print(END_TABLE_3);
  }




END_TABLE_3 常量仅仅是 TD、TR 和 TABLE 结束标记的组合。这种方式似乎有点儿古怪,但是掌握了它之后,就可以用简洁的代码建立良好的 HTML 设计,这意味着只将它用于页面结构,而将尽可能多的样式放在样式表中。
现在完成这个页面:
1
2
out.printEndBox();
out.printPostContent();




第一行结束前面开始的框,printPostContent() 输出页面的其余部分,包括页脚。Picks 表单页面完成了。
处理器 servlet(PicksAction)对提交的 Picks 页面进行响应,它从请求对象收集选择的球队 ID,并将它们传递给业务层来更新适当的 Player 实体,在此之后返回到 Picks 表单页面。这里也执行一项安全检查,确保用户在比赛开始之后无法更新他们的选择。表单和它的处理器都是 servlet,不需要将它们写到单独的界面。它们都使用同样的业务对象来响应参数化的浏览器请求,它们一起构成一个 UI 组件。如果使用 MVC 框架,那么就会将原本简单的事情复杂化了。
返回列表