Shao Fan

关于JAVA与软件工程
posts - 31, comments - 71, trackbacks - 0, articles - 4
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2006年9月8日

目前开发人员对系统开发的一个共识是使用三层架构,分为表示层,业务层,和持久层。而这三层之间的依赖关系如何?比较常见的一种看法是

表示层 --> 业务层 --> 持久层

这表明了层与层之间的调用关系,表示层通过调用业务层来完成任务,而业务层则调用持久层。从另一个角度来看,一种依赖关系是

表示层 --> 领域模型(Domain Model) <-- 持久层

表示层和持久层都应该理解(recognize)领域模型。而领域模型则是业务层的一部分。业务层正是系统的价值所在。虽说表示和持久也很重要,在某些系统中可以说是很关键,但是它们的最终目的都是为业务服务,所以业务层应该是系统的核心

基于以上的认识,在系统设计的时应首先分析需求得到领域模型,找出系统中的实体、对象(静态的一面),并明确大致的业务流程(动态的一面)。 而另两层应尽最大努力为业务层服务,且尽量减少业务层受另两层的限制。


各层的职责:

表示层:负责显示信息,及从系统外部得到输入。表示层的设计决定系统界面的可用性,及信息输入和展示的可靠性。表示层只知道如何展示信息,及收集用户输入,并不知道该如何对这些输入进行处理来完成业务。

业务层:完成业务逻辑。业务层设计决定客户价值是否能够得到实现。这是系统的关键。外在的表现是功能性。业务层设计和实现的失误表现在用户端即功能缺失,功能不可靠等。如果需要对业务层的业务规则进行解耦,则可以使用规则引擎如Drools,把业务规则分离出来。但分离后的业务规则仍属于业务层。业务层知道如何对用户输入进行处理,能够应用业务规则完成用户所需的业务,但它不知道数据如何读取和保存。

持久层:负责用户信息的持久化。持久层的失误表现在外即数据处理(储存,展示等)不可靠。持久层完全不知道业务,只专注于数据存储和读取。所谓持久化并不一定是指数据库,任何方式的持久化(通过文件,网络的持久化等)都应由持久层完成。

各层的设计都会直接影响系统性能。

三层的体积大小和复杂度在不同的系统中可能会有很大的不同。比如说GOOGLE的搜索引擎,它的界面很简单,可以想像表示层是比较容易实现的,而它的业务层,关系到处理关键字,分析搜索结果,决定排名等,而持久层则要负责处理超大量的数据。业务层和持久层则相当复杂。而有的系统持久层会很小,比如杀毒软件,媒体播放软件等。业务层小而另两层大的例子暂时还没有想到:)


posted @ 2007-09-08 19:45 shaofan 阅读(5114) | 评论 (2)编辑 收藏

help是一个内置函数,所谓内置函数,就是在Python中被自动加载的函数,任何时候都可以用。参数分两种:

  • 如果传一个字符串做参数的话,它会自动搜索以这个字符串命名的模块,方法,等。
  • 如果传入的是一个对象,就会显示这个对象的类型的帮助。

比如输入help(’print’),它就会寻找以’print’为名的模块,类,等,找不到就会看到提示信息。而print在python里是一个保留字,和pass,return同等,而非对象,所以help(print)也会出错((kkkkkkk))。

举个例子:

1 help(’sys’) #会列出sys模块的帮助
2 = [1,2,3]
3 help(a) #会显示list的帮助
4 help(a.append) #会显示list的append方法的帮助

python安装自带的library reference,2.1节是关于内置函数的。

Reference Manual的6.6节可以找到关于print的东东。

posted @ 2007-06-05 06:28 shaofan 阅读(2753) | 评论 (0)编辑 收藏

Struts2默认theme是xhtml,它用表格来对表单中的控件进行排版。它也提供一个客户端的js验证功能,但是它的js脚本却有些问题,在某些情况下,前次验证的提示信息无法被清除,提示信息会不断的累积显示在屏幕上。而按照设计,每次提交表单时应只显示每次验证的出错信息。

它的客户端验证的流程大概是这样,用户提交表单时,对各个控件的输入按预先设置的规则进行验证,如果有问题,则清除表单里原有的出错提示信息,并写入新的提示。其设计的功能是把出错信息写表格里出错控件的上方,以便用户看得更加清楚。问题就出在其用来清除原出错信息的函数,其代码是这样的(在struts.jar的template/xhtml目录下可以找到):

 1 function clearErrorMessages(form) {
 2 
 3     var table = form.childNodes[1];
 4     iftypeof table == "undefined" ) {
 5         table = form.childNodes[0];
 6     }
 7 
 8     // clear out any rows with an "errorFor" attribute
 9     var rows = table.rows;
10     var rowsToDelete = new Array();
11     if (rows == null){
12         return;
13     }
14 
15     for(var i = 0; i < rows.length; i++) {
16         var r = rows[i];
17         if (r.getAttribute("errorFor")) {
18             rowsToDelete.push(r);
19         }
20     }
21 
22     // now delete the rows
23     for (var i = 0; i < rowsToDelete.length; i++) {
24         var r = rowsToDelete[i];
25         table.deleteRow(r.rowIndex);
26         //table.removeChild(rowsToDelete[i]);
27     }
28 }


看这个函数的前三行,它试图取得form的第1个或第2个子节点,并把它作为table来处理(看接下来的几行)。要想清除表格里的错误信息,首先要取得表格本身,这没错,但是如果第1个或第2个子节点不是table的话,脚本就会出错,造成原出错信息无法清除,这样每次提交后的提示信息就会累积在屏幕上。

要解决这个问题有两个办法:
  • 写代码时要小心,保证form的第1或2个子节点是table,不要在生成table前加其他代码。
  • 或,修改xhtml的validation.js,使它总能获得正确的table元素,重新打包到struts.jar。
刚看了一下Struts的JIRA,已经有人报告了这个问题(id WW-1802),而且这个bug在2.1版本中已经解决了。

posted @ 2007-06-03 17:56 shaofan 阅读(2527) | 评论 (3)编辑 收藏

假设:用两者写一个最小的WEB程序。
过程可以参照:
1.struts的就太多了,随便哪个都可以
2.python/django可以看limodou写的Django step by step

 

Java/Struts/JSP  Python/Django
开发步骤 1.在web.xml里配置struts的servlet
2.在struts-config.xml里配置URL和action的映射
3.写action
4.写JSP
1.在urls.py里配置URL到方法的映射
2.写相应的方法
3.写HTML模板
调用过程 1.根据web.xml的映射调用struts的servlet controller
2.servlet controller根据struts-config.xml的映射调用相应的action
3.action处理请求
4.JSP渲染显示
1.根据urls.py的映射调用相应的方法
2.方法处理请求
3.HTML渲染显示


相比之下前者用了两层才把一个HTTP请求映射到实际处理的方法:第一次是servlet的映射,第二次是struts action的映射。
而django则一次就从URL映射到相应的方法了。

另外一个比较显著的区别,也是基于java和python的语言上的区别吧,java的所有方法必需包含在一个类中,因此action mapping配置时是映射到类,而action在实现类则应实现事先约定的方法(通过继承或实现接口)。而django则直接得多,可以直接在配置里写明处理请求的方法名。


posted @ 2007-04-06 19:11 shaofan 阅读(4965) | 评论 (0)编辑 收藏

DOM (Document Object Model)是一套语言无关的XML解析的接口定义。它定义了在XML解析中需要的类型,方法,以及属性,比如如何获得一个XML标签,如何改变标签的内容,如何改变它的属性,等等。

DOM只是一个定义,并不是具体的实现,它的目的就是为了让大家在各个平台上都能用相同的方式来处理XML,这样一来,我只要了解DOM,基本上在各个平台上都可以方便的处理XML,而不用重新学习了。比如说,Java, JavaScript, Python都有DOM的实现,用它们来处理XML,方式基本上都是一样的(当然也有非DOM的XML解析方式)。在Java下,实现DOM的类库就有很多,比如JDom,Xerces, 用GOOGLE一搜就一大把。现在Java 5.0内置的就是Xerces。而JavaScript本身就内置了DOM的实现。Python也默认安装了DOM的库。

正因为DOM致力于实现各个平台上对XML一致的处理方式,它定义了一堆自己的接口。因此在用DOM的时候,会有很多非NATIVE的东东。比如说,返回节点的子节点的方法,childNodes,返回的类型是NodeList。我第一次在Java上用,就以为是返回一个List,然后用get(n)方法来取得某元素。而实际上NodeList是用item(n)的方法来取得某元素的。这就让我觉得很怪。而DOM正是用这种方式来获得“语言无关”的能力的。

DOM是用IDL(Interface Definition Language)来定义的。完整的定义可以在这里找到 http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html。IDL也很容易看懂。定义的1.1节列出了所有的接口。

这些接口里,最重要而且常用的是Node,NodeList,Document,Element,Text,Attr这几个。DOM把XML文档看作一棵树,树上的每个元素都是Node。每个Node都属于某个类型,比如Element,attribute,text等。这些类型就表明这个节点在XML文档里的类型了。

比如Node里有个属性:

  readonly attribute unsigned  short   nodeType;

根据这个定义,对于取得的节点,我们就可以通过读取nodeType这个属性来判断这个节点的类型。在Java里,所有的属性都是用getter来取得的,因此对某节点n,就可以用n.getNodeType()取得它的类型。Node接口里也定义了类型常量:

  const unsigned short      ELEMENT_NODE                   = 1;
  const unsigned short      ATTRIBUTE_NODE                 = 2;
  const unsigned short      TEXT_NODE                      = 3;
  const unsigned short      CDATA_SECTION_NODE             = 4;
  const unsigned short      ENTITY_REFERENCE_NODE          = 5;
  const unsigned short      ENTITY_NODE                    = 6;
  const unsigned short      PROCESSING_INSTRUCTION_NODE    = 7;
  const unsigned short      COMMENT_NODE                   = 8;
  const unsigned short      DOCUMENT_NODE                  = 9;
  const unsigned short      DOCUMENT_TYPE_NODE             = 10;
  const unsigned short      DOCUMENT_FRAGMENT_NODE         = 11;
  const unsigned short      NOTATION_NODE                  = 12;

用这些常量和和n.getNodeType()的结果比较,就可以知道它是不是某种类型。

Node接口中也定义了一些方法,比如:

 Node     appendChild(in Node newChild)    raises(DOMException);

表明appendChild方法需要一个Node类型的参数,返回一个Node。 具体的说明可以点文档上的链接进去,也很容易看懂。

Node接口里定义了操纵节点的方法,比如增加子节点,返回父节点,插入新节点,返回节点类型,等等。Document,Element等接口都继承Node接口,因此在它们上面都可以使用操纵节点的方法。

Document:代表整个XML文档。所有DOM元素都不能用类似Java里new的方式来生成,而是要通过调用Document里的相应方法来生成。因此它提供了生成诸如Element, Attr, Text的方法。比如createElement, createTextNode, createComment等。它也提供了名为getElementsByTagName的方法,用来通过标签名称来取得其对象。比如getElementByTagName("ul")就可以获得所有ul标签。它也提供一些文档的属性,比如xmlEncoding,inputEncoding等。它的一个属性,documentElement代表文档的根节点。所有对XML元素的操作,基本上都是从Document开始的。

Element:代表一个XML标签。它可以有属性,子标签,等。比如<ul id="booklist"><li>hello</li></ul>。标签ul是一个Element,它有一个属性叫id,属性的值是booklist。它有一个子结点li。li也是一个标签,它也有个子节点hello,是一个Text类型的节点。这个接口提供操纵其标签属性的方法,比如getAttribute,setAttribute,removeAttribute等。它也提供了和Document中一样的getElementsByTagName的方法,用来获得在这个节点下的元素。

Attr:代表标签中的属性。比如上面的id。它也是一个Node。它有名字,值,也可以获得它的所属标签。

Text:代表一段文字,比如上面的hello,它也一个Node,但比较特殊,它不是直接继承Node,而是继承CharacterData接口,后者继承了Node。但是它不能有子节点。

用JavaScript给一个例子。假设有一个HTML文档:

< html >< head >< title > Try DOM </ title ></ head >< body >
< ul >
< li > hello </ li >
< li > world </ li >
</ ul >
</ body ></ html >

下面是增加一个li的JavaScript方法:

ulList  =  document.getElementsByTagName( " ul " );
ul     
=  ulList.item( 0 );
txt    
=  document.createTextNode( " I am new li " );
li     
=  document.createElement( " li " );
li.appendChild(txt);
ul.appendChild(li);

用Java来写,是这样:

NodeList ulList  =  document.getElementsByTagName( " ul " );
Node        ul      
=  ulList.item( 0 );
Text          txt      
=  document.createTextNode( " I am new li " );
Element    li        
=  document.createElement( " li " );
li.appendChild(txt);
ul.appendChild(li);

可以看到处理方式和数据类型都是一样的。如果要了解更多,可以看看DOM的定义,都是IDL。

posted @ 2007-04-01 18:45 shaofan 阅读(844) | 评论 (0)编辑 收藏

我的博客已搬家,请移步到 http://shao-fan.com/blog 阅读最新内容!


Ubuntu/Debian中的update-alternative用来对系统中不同版本的同个软件进行管理。
比如,系统中可能装有GNU的Java编译器,和SUN的Java编译器。可以用update-alternatives来设置当前使用它们中的哪一个。

它的原理是在/usr/bin中建立一个link,指向/etc/alternatives中的一个文件,而些文件又是一个link,指向当前使用的命令。比如java命令,查看如下:

 $ which java
/usr/bin/java
$ ls -l /usr/bin/java
lrwxrwxrwx /usr/bin/java -> /etc/alternatives/java
$ ls -l /etc/alternatives/java
lrwxrwxrwx /etc/alternatives/java -> /usr/lib/j2re1.5-sun/bin/java
$ ls -l /usr/lib/j2re1.5-sun/bin/java
-rwxr-xr-x /usr/lib/j2re1.5-sun/bin/java

参数--display可以某个软件的当前配置,如:

 $ /usr/sbin/update-alternatives --display java
java - status is auto.
link currently points to /usr/lib/j2re1.5-sun/bin/java
/usr/lib/kaffe/bin/java - priority 300
slave java.1.gz: /usr/share/man/man1/java.kaffe.1.gz
/usr/lib/j2re1.5-sun/bin/java - priority 315
slave java.1.gz: /usr/lib/j2re1.5-sun/man/man1/java.1.gz
Current `best' version is /usr/lib/j2re1.5-sun/bin/java.

status有auto和manual两种。一旦用户更改了系统的默认设置,它就变为manual。在auto的状态下,系统会根据几套配置的priority来判断当前应该使用哪套配置。

每套配置可以设定多个link,它们被称为slave。上面的例子中,有两套java的配置。一套是/usr/lib/kafe/bin/java,另一套是/usr/lib/j2re1.5-sun/bin/java,它们各有一个slave。在些例中这些slave设置的是java命令的 manual。当更改了配置时,用man命令查看的帮助也会相应更改。

参数--config可以用来更改当前的配置。

 $ sudo /usr/sbin/update-alternatives --config java 
There are 2 alternatives which provide `java'.
Selection Alternative
-----------------------------------------------
1 /usr/lib/kaffe/bin/java
*+ 2 /usr/lib/j2re1.5-sun/bin/java
Press enter to keep the default*, or type selection number: 1
Using `/usr/lib/kaffe/bin/java' to provide `java'.

参数--install用来设置一套新的配置。具体参见 http://blog.stevenkroon.com/2006/08/29/debian-update-alternatives/

posted @ 2006-12-25 01:43 shaofan 阅读(6302) | 评论 (1)编辑 收藏

Web开发真是越来越有意思了。现在居然可以在JavaScript里直接调用Java写的方法。大水牛Buffalo的最新版1.2.3发行离现在已有半年时间了,现在才注意到。在客户端的代码相当简单:

buffalo.remoteCall("userService.listAll",[],function(reply){
 // 不用担心,reply.getResult会从聪明的判断服务器端远程调用的结果类型。
 var userList = reply.getResult(); 
 var firstUserFamilyName = userList[0].name.familyName;
});

给Web程序加上Buffalo也相当容易。只要下载几个jar文件放到lib目录下,外加几个JS文件,然后在web.xml里加一个Servlet即可。用的时候是需要有个properties文件来定义哪些JAVA方法可以被JS调用。而服务器端的JAVA代码不需要做任何改动。

暂时手头上还想不起来哪些地方会用,但是这个东东可是我很久以前曾经想过的,现在有人实现了,还是很激动人心的啊。Buffalo的主页是 http://www.amowa.net/buffalo/zh/index.html 。


posted @ 2006-09-08 03:59 shaofan 阅读(8907) | 评论 (6)编辑 收藏