作者:lance lavandowska 编译:blueski
如果你经常去servlet或jsp的新闻组或者邮件列表,那么一定会看到不少关于model i 和model ii 方法的讨论。究竟采用哪一种,这取决于你的个人喜好、团队工作策略以及是否采用正统的oop。
简单地说,model i将事务逻辑(business logic)和表示代码(presentation code)融合在一起(如在html中);model ii则提倡最大限度地将所有的代码放到内容表示之外。
model i: 简单的单层次应用
如果是在一个人人都精通java和html的环境中,或者你独自做着所有的工作,假如每个人都有清晰的编程结构和思路,那么这种方法会很有效,不过这样的假设不在本文讨论范围之内。这种方法的第一个优点是如果你的应用改变了,你只需维护一个文件。而最大的缺陷是可读性!除非十分小心,否则你的html和java代码会相互混杂,从而难以维护。
在下面这个例子中,我们将增加一个 timezone 元素,从而使它变成jsp文件,它会返回基于时间的所期待的timezone。如果没有提交 timezone,那么缺省的是服务器的缺省时间。
======================================================================
<xml version="1.0" ?>
<h1>time jsp</h1>
<jsp:scriptlet>
//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)
timezone timezone = timezone.getdefault(); //returns the default timezone for the server
if (request.getparametervalues("zone") != null)
{
string timezonearg = request.getparametervalues("zone")[0];
timezone = timezone.gettimezone("gmt+" + timezonearg + ":00");
// gets a timezone. for this example we´re just going to assume
// its a positive argument, not a negative one.
}
//since we´re basing our time from gmt, we´ll set our locale to brittania, and get a calendar.
calendar mycalendar = calendar.getinstance(timezone, locale.uk);
</jsp:scriptlet>
<%= mycalendar.get(calendar.hour_of_day) %>:
<%= mycalendar.get(calendar.minute) %>:
<%= mycalendar.get(calendar.second) %>
======================================================================
相应地,数据也可以从javabean取得并加以显示。在下一个例子中我们就可以看到。
model ii: 重定向请求(redirecting requests)
在一个团队开发环境中,有些是html设计者,另一些则是java程序员,这时这一方法显得非常重要。java程序员可以集中精力创建可重用代码,而html设计师可以集中精力于内容表示,彼此相对对立,可以分别动态地修改自己的内容,只要总体的输入输出不变。
现在我们可以使用model ii来表示model i的那个例子。这一方法遵循了model-view-controller (mvc) 范例 (cite design patterns book)。 在这个例子中,我们只有一个类(页或者servlet) 处理请求(controller),取得timezone,设置所有用于表示的变量,并将控制传递到表示页(view)。作为如此简单的应用,可以没有 "model"。
controller: timebyzone.jsp
controller可以是一个servlet或一个jsp页。我推荐使用jsp,因为这样我不必担心每当我做修改时要对类重新编译,但是,你将因此失去granularity(颗粒性),以后要扩展该类也比较困难。
======================================================================
<xml version="1.0" ?>
<!–worker class, nobody should see me–>
<jsp:scriptlet>
//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)
timezone timezone = timezone.getdefault(); //returns the default timezone for the server
if (request.getparametervalues("zone") != null)
{
string timezonearg = request.getparametervalues("zone")[0];
timezone = timezone.gettimezone("gmt+" + timezonearg + ":00");
// gets a timezone. for this example we´re just going to assume
// its a positive argument, not a negative one.
}
timebean timebean = new timebean();
timebean.sethours = mycalendar.get(calendar.hour_of_day);
timebean.setminutes = mycalendar.get(calendar.minute);
timebean.setseconds = mycalendar.get(calendar.second);
httpsession mysession = request.getsession();
mysession.putvalue("temptimebean", timebean);
</jsp:scriptlet>
<jsp:forward page="displaytime.jsp" />
======================================================================
view: displaytime.jsp
同样地,这个view既可以是一个servlet也可以是一个jsp文件。这里我们从session中取得并显示它的值。实际上我们会将这做两次,来示范bean是如何被使用的。
======================================================================
<xml version="1.0" ?>
<h1>time jsp</h1>
<jsp:usebean class="timebean" id="temptimebean" scope="session" />
<jsp:getproperty name="temptimebean" property="hours">:
<jsp:getproperty name="temptimebean" property="minutes">:
<jsp:getproperty name="temptimebean" property="seconds">
<!– these would have printed "null" if temptimebean was not instantiated by timebyzone.jsp –>
<jsp:scriptlet>
httpsession mysession = request.getsession();
timebean timebean = mysession.getvalue("temptimebean");
if (timebean != null)
{ // check to make sure its not null, to avoid nullpointerexceptions
out.print(timebean.gethours());
out.print(":");
out.print(timebean.getminutes());
out.print(":");
out.print(timebean.getseconds());
}
else
{
out.println("press your back button and select a timezone");
}
</jsp:scriptlet>
======================================================================
第二种方法(在内部使用了代码)可能有些笨重,但允许开发者确保输出不至于很糟糕(例如"null:null:null null"),假定session bean还没有被实例化以及没有进行值的设置。 这种情况发生在客户端直接调用了view页。问题是使用脚本scriptlets可以允许更强的控制。如果你确信你可以控制url存取,那么bean方法当然更适合于开发,并使 view页更方便于html设计者的协同工作。
上面的是"传统的" model ii设计。所有的变量都包装了并放在session对象中。这有2个不足:
1) 如果客户端拒绝参与的话,session是不可得到的。
2) 除非session变量被显式地移走,否则它回一直存在,直到session被破坏或过期。
第一种案例很可能发生在这样的场合,即使用了cookies作为声明的结构(mechanism)而开发者没有能够提供声明的结构的替代表单(form),即url改写。
第二个案例甚至更为严重,因为它可能引起很大的内存消耗,如果sessions被定义为保存比标准存留时间更长的话((标准存留时间是30分钟)。即使是30分钟的session,这种model也可能在大的应用中引起灾难性的内存泄露。为什么呢?在session对象内部设置的对象被实例化了,并且在session终止以前一直没有被移去。因为它们仍然有关联references(session对象) 指向它们,所以无法被垃圾收集(garbage-collected)。在model ii 模型中,很多对象被放到session中(要么直接地,要么通过javabean)。随着session的进行,更多的页被存取,内存使用会增加并持续下去直到客户端终止了session或者session过期。要一直等到session变得非法,放在那的对象才能被垃圾收集,而那些损失的内存本可以用于任何其它的用途。.
改进的方法之一是将beans或者其它变量放到request对象中去,并使用requestdispatcher.include()而不是requestdispatcher.forward()。这样做以后,view 页具有和controller一样的存取请求的对象。传统的model ii设计的不足可以被排除。
一个最后的评注:尽管有如上所述,我个人仍有些不喜欢model ii 的范例,如果它用通常方法开发的话。 客户端被引送到某一个地址,然后又被转向到另一个不同的类,我不喜欢创建这样的系统。基于这样的原因,我修改了设计,使它变成了以下的样子:
controller: timebyzone2.jsp
和前面一样,controller使用request值来取得必要的数据,并且将数据放到请求的对象中去。这回的区别是view页将使用requestdispatcher.include()来调用controller。在这种方法中,客户端再也不做重定向,请求不是“链接chained”的。相当于class/jsp请求了另一方来为它做一些工作,然后继续。
======================================================================
<xml version="1.0" ?>
<!–worker class, nobody should see me–>
<jsp:scriptlet>
//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)
timezone timezone = timezone.getdefault(); //returns the default timezone for the server
if (request.getparametervalues("zone") != null)
{
string timezonearg = request.getparametervalues("zone")[0];
timezone = timezone.gettimezone("gmt+" + timezonearg + ":00");
// gets a timezone. for this example we´re just going to assume
// its a positive argument, not a negative one.
}
timebean timebean = new timebean();
timebean.sethours = mycalendar.get(calendar.hour_of_day);
timebean.setminutes = mycalendar.get(calendar.minute);
timebean.setseconds = mycalendar.get(calendar.second);
request.setattribute("temptimebean", timebean);
</jsp:scriptlet>
======================================================================
view: displaytime2.jsp
和displaytime.jsp非常相似,但timebyzone2.jsp在也的顶部被调用。请注意 <jsp:usebean /> 中的"scope"已经被换成了"request"。
======================================================================
<xml version="1.0" ?>
<h1>time jsp</h1>
<jsp:include page="timebyzone2.jsp" />
<jsp:usebean class="timebean" id="temptimebean" scope="request" />
<jsp:getproperty name="temptimebean" property="hours">:
<jsp:getproperty name="temptimebean" property="minutes">:
<jsp:getproperty name="temptimebean" property="seconds">
<!– these would have printed "null" if temptimebean was not instantiated by timebyzone2.jsp –>
======================================================================
在一个在建系统中,我们已经使用这种方法来创建类的链,每一个都只对它所处理的工作负责。通过辨别公用的表示格式,我们创建了一个view对象,即使在很高层次的jsp中它也可以重复使用。我们的目标就是建立一些可重用的页,同时减少用于表示的类的数量。
单个的servlet model (a model ii design)
什么时候我有足够时间来研究这个课题,我会在这里发表更多的东西。
附原文:
jsp architectures
an explanation and comparison of the methodologies
commonly known as "model i" and "model ii".
lance lavandowska to outline
if you spend any time reading through servlet or jsp related newsgroups or mailing lists, you´re likely to encounter a discussion of model i versus model ii methodologies . which one you use depends on personal taste, team work strategies and oop orthodoxy.
loosely described, model i is an approach where business logic and presentation code can be intermixed with the presentation itself (html in our arena). model ii proscribes that all code, to the extent this is possible, be excluded from the presentation.
model i: simple 2 1/2 tier application
in a team environment where everyone knows java and html, or if you´re doing it all yourself, this approach can work well, provided everyone maintains a clear coding structure (that discussion is outside the bounds of this article). the primary advantage of this approach is that there is only one file to maintain for changes to your application. the major disadvantage is readability! unless great care is taken, your html and java code can become so intermingled that it becomes difficult to debug and maintain your application.
for this example, we are going to revisit the "sample page" from the jsp quick start chapter. i´m going to add a timezone element, so we´ll have a jsp that returns the time based on the desired timezone. if no timezone is submitted, we´ll default to that of the server.
======================================================================
<xml version="1.0" ?>
<h1>time jsp</h1>
<jsp:scriptlet>
//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)
timezone timezone = timezone.getdefault(); //returns the default timezone for the server
if (request.getparametervalues("zone") != null)
{
string timezonearg = request.getparametervalues("zone")[0];
timezone = timezone.gettimezone("gmt+" + timezonearg + ":00");
// gets a timezone. for this example we´re just going to assume
// its a positive argument, not a negative one.
}
//since we´re basing our time from gmt, we´ll set our locale to brittania, and get a calendar.
calendar mycalendar = calendar.getinstance(timezone, locale.uk);
</jsp:scriptlet>
<%= mycalendar.get(calendar.hour_of_day) %>:
<%= mycalendar.get(calendar.minute) %>:
<%= mycalendar.get(calendar.second) %>
======================================================================
similarly, the data to be displayed could have been gotten from a javabean. we´ll see a little of that in the next example.
model ii: redirecting requests
in a team environment where some members are html designers and others are java programmers, this approach can be particularly strong. the java programmers can focus on creating (re)usable code, while the html designers can focus on presentation. while the two remain dependant on each other, one or the other can change dramatically so long as the principle inputs and outputs (respectively) remain the same.
now we´ll take the same desired behaviour from the model i example, and present it using the model ii methodology. this methodology follows the model-view-controller (mvc) paradigm (cite design patterns book). for this example, we´ll have one class (or page or servlet) process the request (controller), get the timezone, set all the required variables for presentation, and pass control off to a presentation page (view). for simple apps like this, there is no "model".
controller: timebyzone.jsp
the controller can be a servlet or a jsp file. i prefer to use jsp, as i don´t have to worry about compiling the class each time i make changes. however, you lose granularity this way, and make it more difficult to extend this class later (we´ll review this in advanced jsp programming).
======================================================================
<xml version="1.0" ?>
<!–worker class, nobody should see me–>
<jsp:scriptlet>
//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)
timezone timezone = timezone.getdefault(); //returns the default timezone for the server
if (request.getparametervalues("zone") != null)
{
string timezonearg = request.getparametervalues("zone")[0];
timezone = timezone.gettimezone("gmt+" + timezonearg + ":00");
// gets a timezone. for this example we´re just going to assume
// its a positive argument, not a negative one.
}
timebean timebean = new timebean();
timebean.sethours = mycalendar.get(calendar.hour_of_day);
timebean.setminutes = mycalendar.get(calendar.minute);
timebean.setseconds = mycalendar.get(calendar.second);
httpsession mysession = request.getsession();
mysession.putvalue("temptimebean", timebean);
</jsp:scriptlet>
<jsp:forward page="displaytime.jsp" />
======================================================================
view: displaytime.jsp
again, the view can be either a servlet or a jsp file. here we´ll get the bean from the session, and display its values. we´ll actually do this twice, to illustrate again how beans are used.
======================================================================
<xml version="1.0" ?>
<h1>time jsp</h1>
<jsp:usebean class="timebean" id="temptimebean" scope="session" />
<jsp:getproperty name="temptimebean" property="hours">:
<jsp:getproperty name="temptimebean" property="minutes">:
<jsp:getproperty name="temptimebean" property="seconds">
<!– these would have printed "null" if temptimebean was not instantiated by timebyzone.jsp –>
<jsp:scriptlet>
httpsession mysession = request.getsession();
timebean timebean = mysession.getvalue("temptimebean");
if (timebean != null)
{ // check to make sure its not null, to avoid nullpointerexceptions
out.print(timebean.gethours());
out.print(":");
out.print(timebean.getminutes());
out.print(":");
out.print(timebean.getseconds());
}
else
{
out.println("press your back button and select a timezone");
}
</jsp:scriptlet>
======================================================================
the second method (using code inside) may be more cumbersome, but allows the developer to ensure against ugly output (such as "null:null:null null") if the session bean has not been instantiated & had its values set. this would likely only happen if the client somehow called the view page directly. the point is that using scriptlets allows for greater control. if you are certain you can control url access, the bean approach certainly eases development, and makes the view page easier for html designers to work with.
the above is the "traditional" model ii design. you´ll note that all the variables are wrapped up and placed into the session object. this has two weaknesses: 1) no session is availabe because the client has refused to participate, 2) unless the session variable is explicitly removed it will continue to exist until the session is destroyed or expires.
the first case is most likely to happen when cookies are used as the state mechanism and the developers have failed to provide for the alternative form of state maintenance, url rewriting.
the second case is even more serious, as it can lead to great memory use if sessions are defined to exist for more than the standard amount of time (30 minutes appears to be the standard). even in the case of 30 minute sessions, this model can lead to disastrous memory leaks in systems under great use. why? objects get instantiated, set inside the session object, and are not removed until the session ends. because they still have references (the session object) pointing to them, they are not garbage-collected. in the model ii pattern, many objects are placed into the session (either directly or via a javabean). as the session progresses (more pages are accessed) memory-use increases and persists until the client ends the session or the session times out. until the session is invalidated, the objects placed there cannot be garbage-collected, and thus consume memory that could be of use elsewhere.
one means of addressing this issue is to place the beans or other variables into the request object, and use requestdispatcher.include() rather than requestdispatcher.forward(). by doing so, the view page has access to the same request object as the controller, and the weaknesses of the traditional model ii design are obviated.
one final comment: despite all the above, i have a personal distaste for the model ii paradigm as it is commonly implemented. the creation of a system where the client is sent to an address, but is redirected to a different class, is for some reason abhorrent to me. for this reason, i´ve modified the design in the following manner:
controller: timebyzone2.jsp
as before, the controller uses the request values to obtain the necessary data and put that data into the request object. the difference this time is that the view page will call the controller using requestdispatcher.include(). in this way, the client is never redirected, and requests are not "chained". rather, the class/jsp called asks someone else to do some work for it, then continues.
======================================================================
<xml version="1.0" ?>
<!–worker class, nobody should see me–>
<jsp:scriptlet>
//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)
timezone timezone = timezone.getdefault(); //returns the default timezone for the server
if (request.getparametervalues("zone") != null)
{
string timezonearg = request.getparametervalues("zone")[0];
timezone = timezone.gettimezone("gmt+" + timezonearg + ":00");
// gets a timezone. for this example we´re just going to assume
// its a positive argument, not a negative one.
}
timebean timebean = new timebean();
timebean.sethours = mycalendar.get(calendar.hour_of_day);
timebean.setminutes = mycalendar.get(calendar.minute);
timebean.setseconds = mycalendar.get(calendar.second);
request.setattribute("temptimebean", timebean);
</jsp:scriptlet>
======================================================================
view: displaytime2.jsp
much like displaytime.jsp, however you´ll see that timebyzone2.jsp is called at the top of the page. notice that the scope of <jsp:usebean /> has changed to "request".
======================================================================
<xml version="1.0" ?>
<h1>time jsp</h1>
<jsp:include page="timebyzone2.jsp" />
<jsp:usebean class="timebean" id="temptimebean" scope="request" />
<jsp:getproperty name="temptimebean" property="hours">:
<jsp:getproperty name="temptimebean" property="minutes">:
<jsp:getproperty name="temptimebean" property="seconds">
<!– these would have printed "null" if temptimebean was not instantiated by timebyzone2.jsp –>
======================================================================
in a system currently under construction, we´ve made use of this method to create chains of classes, each responsible for its own processing. by identifying common presentation formats, we´ve created view objects that can be reused in yet higher level javaserver pages. our goal is to create pages that are designed for reuse, and to reduce the number of presentation classes.
single servlet model (a model ii design)
when i´ve had time to adequately research and implement this idea, i´ll post something here.