开发 Library
“checkout book” 和“delete book”的用例如下:
Checkout book
说明:一个member能借出一项资源。Library记录借出的日期,而且要求member在预定的日期(dueDate)内返回资源。
前提条件:
Member在library中进行注册
Member没有走出归还日期仍没有归还的书
Member所借出的资源不走出5本
Book现有没有被借出
完成结果:
资源被member借出
资源预定归还日期已设定
资源借出日期已记录
Delete book
说明:管理者可以从library中删除book
前提条件:
Book当前没有被借出
完成结果:
Book被永远从library中删除
Delete member
说明:管理员可以从library中删除member
前提条件:
当前member没有借出任何资源
完成结果:
Member被永远从系统中删除
使用 MVC 实现借书( checkout )
MVC应用流程如图1所示,checkout的事务处理从controller开始。图书馆中的每本书者由一个Notes文档来表示,代表书的Notes文档的unid提交至checkInOut代理(controller),由代理对checkout事务进行调用,因为每个代理都有一定数量的初始化和结束代码,所以减少代码数就是减少维护量.由此,仅用一个checkInOut控制器处理checkout和checkin操作。
Action参数决定了执行是checkout还是checkin。多个固定在单个控制器上也是J2EE中的一个普遍应用。
事务由book表单上的action进行调用。Action传送一个HTTP get请求,如:
http://192.168.1.10/library.nsf.checkInOut?OpenAgent&action=out&unid=XXXXXXXXXXXX 。
checkInOut 代理的代码如下列表1。其执行了如下的任务:
public class Agent_CheckInOut extends AgentBase{
public void NotesMain{
try{
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
Document docContext = agentContext.getDocumentContext();
Database dbCurrent = agentContext.getCurrentDatabase();
//get params
HTTPAgentRequest request = new HTTPAgentTRequest(docContext);
String strUNID = request.getParameter(“unid”);
String strAction = request.getParameter(“action“);
//get book document
Document docBook = dbCurrent.getDocumentByUNID(strUNID);
//create Book document
Book book = new Book(docBook);
//create member business
//object for current user
Member member = Member.createMemberFromCurrentUser();
//perform check in or out
//based on action param
if(strAction.eqauals(“out”)) book.checkOut(member);
else if(strAction.equals(“in”)) book.checkIn();
//redirect user to book in read mode
String strUrl = book.getReadModeURL();
getAgentOutput().println(”[” + strUrl + “]”);
}catch (Exception e){
ExceptionHAndler.handleException(e);
}
}
}
1、 从查询字符串中读取action和unid。HTTPAgentRequest是作为一个Helper class类(模仿HttpServletRequest)以更易获取查询字符串。
2、 通过unid找到相应的Notes文档。这就是将要被checkout的book文档。
3、 根据book文档创建一个book业务对象。
4、 根据生成代表当前web使用者的member对象的静态方法创建一个Member业务对象。CheckOut代理需要启用“作为Web用户运行代理”的设置。
5、 对book对象调用checkOut()方法,作为当前使用者checkout这本书。这就是调用 model层所产生的效果。
6、 checkOut()完成之后,代理使book文档对使用者为只读状态。getReadModeURL() 是在BusinessDocument基类中定义的helper method,返回一个原始Notes文档的只读状态的Document URL。这就是调用View层所产生的效果。
book.checkOut()方法执行了如下的任务:
1、 分别为checkout时间和due时间创建lotus.domino.DateTime对象。
2、 写入新的域值checkout。Library中的每一篇文档都拥有一个唯一的关键字,叫做docID,它是对创建文档时所生成的UNID的稍加更改的计算值。为把book和需要借书的member联系起来,book中存储了member文档的DocID。通过这种方式,每一本book都可以找到借出它的member,而且每一个member也可以找到各自借出的book。
3、 保存文档save()虽然实质上就是执行了标准方法doc.save(true,false),但其中也包括在服务器文档中已启用“允许运行当前代理”的条件下某些用来构造线程安全(thread-safe)的逻辑。在业务对象初始化这段时间内,如果文档已经更改,那么save()方法将操作失败(抛出一个异常)。由于save()是由基类(BusinessDocument)来实现的,所以所有业务对象都拥有安全的特性。
checkout()方法如列表2。
使用 Factory 和 Objectifier 模式调用公共方法
Member和book都拥有deleter()方法,这些方法都对BusinessDocument的delete()方法进行了重载。许多应用都拥有如deleter()和webQuerySave()这样的多个业务对象所共有的公共方法。你可使用Factory和objectifier模式使控制器(agent/servlets)可以有效地使用这些函数。
在到程序执行才能知道对象类型(类)的情况下,你可以使用Factory模式来进行对象的创建。你可以使用带有创建对象的静态方法的类来实现这个模式,其他类仅仅是调用这个静态方法来创建对象。
public void checkOut(Member member) throws Exception{
//compute new dates
DateTime dtOut = _global.getSession().createDateTime(new java.util.Date());
DateTime dtDue = _global.getSession().createDateTime(new java.util.Date());
dtDue.adjust(14);
//perform check-out
_doc.replaceItemValue("Status",STATUS_OUT);
_doc.replaceItemValue("MemberName",member.getDoc().getItemValuteString("UserName"));
_doc.replaceItemValue("MemberID",member.getDoc().getItemValueString("DocID"));
_doc.replaceItemValue("DateOut",dtOut);
_doc.replaceItemValue("DateDue",dtDue);
save();
}}
列表3:
public final class BusinessObjectFactory {
public static BusinessDocument createBusinessObject(Document doc) throws Exception{
//get form name
String strForm = doc.getItemValueString("Form");
//init return object
BusinessDocument bdoc = null ;
//create appropriate BusinessDocument subclass
//based on form name. Create a BusinessDocument if
//no class applies.
if (strForm.equals(Global.FORM_BOOK))
bdoc = new Book(doc);
else if (strForm.equals(Global.FORM_MEMBER))
bdoc = new Memeber(doc);
else
bdoc = new BusinessDocument(doc);
return bdoc;
}
}
根据Notes文档的数据,你可以使用Factory模式创建一个业务对象(book或member)。BusinessObject Factory返回一个book或member对象,并把这个对象作为BusinessDocument的一个可变类型。在面向对象语言中(包括面向对象LotusScript),你可以在定义了超类的任何地方使用子类,因为子类一定拥有超类所有的公共属性和方法。
BusinessObjectFactory代码如列表3。
Objectifier模式允许一个子类对所有给的函数进行选择性重载,以执行这个函数的待定需求。在这个模式中,定义方法来调用表示父类类型的变量,但这个子类是在runtime时确定的,并对这个方法进行了重载以执行选定的需求。
首先,看看在不同使用这些模式的时候是如何调用delete()方法的。似通过个Delete代理处理member和book的删除操作,代码如列表4。
列表 4:
。。。
//get document and form
Document doc = dbCurrent.getDocumentByUNID(strUNID);
String strForm = doc.getItemValueString("Form");
//instantiate appropriate object based on form and perform deletion
if(strForm.equals(Global.FORM_BOOK)){
Book book = new Book(doc);
}else if(strForm.equals(Global.FORM_MEMBER)){
Member member = new Member(doc);
member.deleter();
}
使用Factory和objectifier模式完成相同的任务,代码如列表5。
列表5:
…
//Create business object
Document doc = dbCurrent.getDocumentBUNID(strUNID);
BusinessDocument bdoc = BusinessObjectFactory.createBusinessObject(doc);
//perform deletion
bdoc.delete();
…
BusinessObjectFactory.createBusinessObject(doc)返回一个book或member对象,且的对象为超类BusinessDocument的一个变量类型。它对delete()进行调用。
除了代理中只需较少的代码之外,使用这些模式的好处主要体现在维护上。第一种方法中,form-to-business-object的匹配过程体现在代理中。当你需要在应用中增加新的对象类型,例如video,你必须更新为个匹配过程。而且,另一个执行公共任务(如webQuerySave())的代理也必须包含同样的匹配过程。把Factory和Objectfier相结合,你就可以随意 增加或删除新的文档类型,而不用对代理作出修改。
表3 : library 执行所有的方法和过程
用例 | 执行过程 |
创建 book 更新 book 注册 member 更新 member | l 文档的创建与更新通过表单和webQuerySave代理来处理。库对所有的表单都使用一个WebQuerySave代理 l 在文档被保存时,代理使用factory和objectifier模式对相应的业务对象调用 webQuerySave() l 业务对象能够对webQuerySave()进行重载,以执行对业务对象的验证或操作 l Member和Book中的webQuerySave()方法都确保创建和更新的用例的预处理应在保存进程进行之前。 |
删除 book 删除 member | l Delete代理(作为MVC删除控制器)使用Factory和Objectifier模式对于已经建立的member或book对象调用delete() l book和member类的deleter()方法使各自的预处理生效,并调用 super.delete()执行删除。 |
Checkout book Checkin book | Checkout代理(作为进行checkout和checkin的模式MVC控制器)创建book对象,并根据所得参数“action”的值来调用checkout或checkin方法 |
其他用例的实现
表3 总结了library执行所有用例的方法和过程。
设计模式的力量
MVC设计模式把表现层代码从业务逻辑中分离出来,Domino中使用MVC可以简化对UI的维护并为应用功能提供了可选的多界面。
Domino应用可以通过某些某些机制(包括ODBC和JDBC驱动器)使它们的数据对于其它系统可用。赋闲的,MVC模式可以使应用实现的功能对于系统可用,除了为web浏览器服务之外,应用可以向web services、WAP、CORBA和其它多种类型的客户端提供服务。
虽然有很多途径用来组织model层,但是通过使用一个java类且表现出现实世界对象的模式是最好的选择。J2EE中,现实世界的对象通过 enterprise session beans和enterprise entity beans来表现,enterprise session beans包含业务逻辑,而enterprise entity beans包含持久数据。在Domino中,业务对象包含了业务逻辑,Notes文档包含了稳定的数据。
如果使用业务对象实现编码,对于越复杂的应用,你将会得到 越多的益处。业务对象能够封装与现实世界的对象相关的所有任务和业务规则。与现实世界对象相关的功能代码不 能在不同的代理里面进行跨跃。
把代码封装进一个对象之后,你可以使用熟悉的设计模式来解决公共问题,对于调用多业务对象(例如删除、更新或批准操作)的公共操作,Factory和Objectifier模式提供了很有效的方法。