学习日记V0.9.0.4正式推出!

  学习日记V0.9.0.4正式推出!下载地址:http://www.learndiary.com/download/learndiaryV0.9.0.4.war。在learndiaryV0.9.0.3的基础上,有如下主要的变化:

  1)、完善网站的动态导航系统;

  2)、根据功能拆分原来的数据库存取对象,但这不是一个真正的DAO方案;

  3)、初步改善登录模式;

  4)、数据库改为utf-8编码,你可以用任意语言书写自己的学习日记;

  5)、修复了一些bug。

  最新的源代码在我们开发社区的CVS库中随时更新(地址:http://learndiary.tigris.org/source/browse/learndiary/old)。欢迎有志于开源事业的Java爱好者加入到我们的开发队伍。

  我们将本着应用为先,用户至上的原则继续向前发展,为最终实现建设一个具有普遍适用性的开源网络学习交流平台而努力。

欣赏一篇关于开源软件的好帖(网友推荐转帖)

前车之覆,后车之鉴 --开源项目经验谈

qinpt 转贴  (参与分:26557,专家分:2297)   发表:2005-08-01 18:08   版本:1.0   阅读:215次 

 

 

 

(本文发表于《程序员》2005年第2期)

    随着开源文化的日益普及,“参与开源”似乎也变成了一种时尚。一时间,似乎大家都乐于把自己的代码拿出来分享了。就在新年前夕,我的一位老朋友、一位向来对开源嗤之以鼻的J2EE架构师竟然也发布了一个开源的J2EE应用框架(姑且称之为“X框架”),不得不令我惊叹开源文化的影响力之强大。

    可惜开源并非免费的午餐,把源码公开就意味着要承受众目睽睽的审视。仅仅几天之后,国内几位资深的J2EE架构师就得出一个结论:细看之下,X框架不管从哪个角度都只能算一个失败的开源项目。究竟是什么原因让一个良好的愿望最终只能得到一个失败的结果?本文便以X框架为例,点评初涉开源的项目领导者常犯的一些错误,指出投身开源应当遵循的一些原则,为后来的开源爱好者扫清些许障碍。

成熟度

    打开X框架在SourceForge的项目站点,我们立刻可以看到:在“Development Status”一栏赫然写着“5 ? Production/Stable”。也就是说,作者认为X框架已经成熟稳定,可以交付用户使用。那么,现在对其进行评估便不应该有为时过早之嫌。可是,X框架真的已经做好准备了吗?

    打开从SourceForge下载的X框架的源码包,笔者不禁大吃一惊:压缩包里真的只有源码??编译、运行整个项目所需的库文件全都不在其中。从作者自己的论坛得知,该项目需要依赖JBoss、JDOM、Castor、Hibernate等诸多开源项目,笔者只好自己动手下载了这些项目,好一番折腾总算是在Eclipse中成功编译了整个项目。

    不需要对开源文化有多么深刻的了解,只要曾经用过一些主流的开源产品,你就应该知道:一个开源软件至少应该同时提供源码发布包和二进制发布包,源码包中至少应该有所有必需的依赖库文件(或者把依赖库单独打包发布)、完整的单元测试用例(对于Java项目通常是Junit测试套件)、以及执行编译构建的脚本(对于Java项目通常是Ant脚本或者Maven脚本),但这些内容在X框架的发布包中全都不见踪影。用户如果想要使用这个框架,就必须像笔者一样手工下载所有的依赖库,然后手工完成编译和构建,而且构建完成之后也无从知晓其中是否有错误存在(因为没有单元测试)。这样的发布形式,算得上是“Production/Stable”吗?

开源必读:便捷构建

     开源软件应该提供最便捷的构建方式,让用户可以只输入一条命令就完成整个项目的编译、构建和测试,并得到可运行的二进制程序。对于Java项目,这通常意味着提供完整的JUnit测试套件和Ant脚本。你的潜在用户可能会在一天之内试用所有类似的开源软件,如果一个软件需要他用半天时间才能完成构建、而且还无从验证正确性、无从着手编写他自己的测试用例,这个软件很可能在第一时间被扔到墙角。

 

     从SourceForge的项目页面可以看到,X框架的授权协议是Apache License V2.0(APL)。然而在它的发布包中,笔者没有看到任何形式的正式授权协议文本。众所周知,SourceForge的项目描述是可以随时修改的(X框架本身的授权协议就曾经是GPL),如果发布包中没有一份正式的授权协议文本,一旦作者修改了SourceForge的项目描述,用户又该到哪里去寻找证据支持自己的合法使用呢?

     在X框架的源码中,大部分源文件在开始处加上了APL的授权声明,但有一部分源码很是令人担心。例如UtilCache这个类,开始处没有任何授权声明,而JavaDoc中则这样声明作者信息:

@author     <a href="mailto:jonesde@ofbiz.org">David E. Jones</a>

也就是说,这个类的源码来自另一个开源项目Ofbiz。值得一提的是,Ofbiz一直是“商业开源”的倡导者,它的授权协议相当严格。凡是使用Ofbiz源码,必须将它的授权协议一并全文复制。像X框架这样复制Ofbiz源码、却删掉了授权协议的行为,实际上已经构成了对Ofbiz的侵权。

    另外,作者打包用的压缩格式是RAR,而这个压缩格式对于商业用户是收费的。对于一个希望在商业项目中应用的框架项目来说,选择这样一个压缩格式实在算不得明智。而且笔者在源码包中还看到了好几个.jbx文件,这是JBuilder的项目描述文件。把这些JBuilder专用的文件放在源码包中,又怎能让那些买不起或是不想买JBuilder的用户放心呢?更何况,出于朋友的关心,笔者还不得不担心X框架的作者是否会收到Borland公司的律师信呢。

开源必读:授权先行

    在启动一个开源项目时,第一件大事就是要确定自己的授权协议,并在最醒目的地方用最正式的方式向所有人声明??当然,在此之前你必须首先了解各种开源授权协议。譬如说,GPL(Linux采用的授权协议)要求在软件之上的扩展和衍生也必须继承GPL,因此这种协议对软件的商业化应用很不友好;相反,APL则允许用户将软件的扩展产物私有化,便于商业应用,却不利于开发者社群的发展。作为一个开源项目的领导者,对于各种授权协议的利弊是不可不知的。

    除了源码本身的授权协议之外,软件需要使用的类库、IDE、解压工具等等都需要考虑授权问题。开源绝对不仅仅意味着“免费使用”,开源社群的人们有着更加强烈的版权意识和法律意识。如果你的开源软件会给用户带来潜在的法律麻烦,它离着被抛弃的命运也就不远了。

 

     可以看到,不管从法律的角度还是从发布形式的角度,X框架都远够不上“Production/Stable”的水准??说实在的,以它的成熟度,顶多只能算是一个尚未计划周全的开源项目。虽然作者在自己的网站上大肆宣传,但作为一个潜在的用户,我不得不冷静地说:即便X框架的技术真的能够吸引我,但它远未成熟的项目形态决定了它根本无法在任何有实际意义的项目中运用。要让商业用户对它产生兴趣,作者需要做的工作还很多。

我刚才说“即便X框架的技术真的能够吸引我”,这算得上是一个合理的假设吗?下面,就让我们进入这个被作者寄予厚望的框架内部,看看它的技术水平吧。

整体架构

在X框架的宣传页面上,我们看到了这样的宣传词:

X框架解决了以往J2EE开发存在的诸多问题:EJB难用、J2EE层次复杂、DTO太乱、Struts绕人、缓存难做性能低等。X框架是Aop/Ico[注:应为“IoC”,此处疑似笔误]的实现,优异的缓存性能是其优点。

下面是X框架的整体架构图:

 

可以看到,在作者推荐的架构中,EJB被作为业务逻辑实现的场所,而POJO被用于实现Fa?ade。这是一个好的技术架构吗?笔者曾在一篇Blog中这样评价它[1]:

让我们先回想一下,使用EJB的理由是什么?常见的答案有:可分布的业务对象;声明性的基础设施服务(例如事务管理)。那么,如果在EJB的上面再加上一 层POJO的Fa?ade,显然你不能再使用EJB的基础设施了,因为完整的业务操作(也就是事务边界)将位于POJO Fa?ade的方法这里,所以你必须重新??以声明性的方式??实现事务管理、安全性管理、remoting、缓存等基础设施服务。换句话说,你失去了 session bean的一半好处。另一方面,“可分布的业务对象”也不复存在,因为POJO本身是不能??像EJB那样??分布的,这样你又失去了session bean的另一半好处。

继续回想,使用基于POJO的轻量级架构的理由是什么?常见的答案有:易于测试;便于移植;“开发-发布”周期短。而如果仅仅把POJO作为一层Fa?ade,把业务逻辑放在下面的EJB,那么你仍然无法轻易地测试业务逻辑,移植自然也无从谈起了,并且每次修改EJB之后必须忍受漫长的发布周期。 即便是仅仅把EJB作为O/R mapping,而不是业务逻辑的居所,你最多只能通过DAO封装获得比较好的业务可测性,但“修改-发布”的周期仍然很长,因为仍然有entity bean存在。也就是说,即使是往最好的方面来说,这个架构至少损失了轻量级架构的一半优点。

作为一个总结,X框架即便是在使用得最恰当的情况下,它仍然不具备轻量级架构的全部优点,至少会对小步前进的敏捷开发造成损害(因为EJB的存在),并且没有Spring框架已经实现的基础设施(例如事务管理、remoting 等),必须重新发明这些轮子;另一方面,它也不具备EJB的任何优点,EJB的声明性基础设施、可分布业务对象等能力它全都不能利用。因此,可以简单地总结说,X框架是一个这样的架构:它结合了EJB和轻量级架构两者各自的短处,却抛弃了两者各自的长处。

在不得不使用EJB的时候,一种常见的架构模式是:用session bean作为Fa?ade,用POJO实现可移植、可测试的业务逻辑。这种模式可以结合EJB和POJO两者的长处。而X框架推荐的架构模式,虽然乍看起来也是依葫芦画瓢,效果却恰恰相反,正可谓是“取其糟粕、去其精华”。

开源必读:架构必须正确

在开源软件的初始阶段,功能可以不完善,代码可以不漂亮,但架构思路必须是正确的。即使你没有完美的实现,参与开源的其他人可以帮助你;但如果架构思路有严重失误,谁都帮不了你。从近两年容器项目的更迭就可以看出端倪:PicoContainer本身只有20个类、数百行代码,但它有清晰而优雅的架构,因此有很多人为它贡献外围的功能;Avalon容器尽管提供了完备的功能,但架构的落伍迫使Apache基金会只能将其全盘废弃。

所以如果你有志于启动一个开源项目(尤其是框架性的项目),务必先把架构思路拿出来给整个社群讨论。只要大家都认可你的架构,你就有机会得到很多的帮助;反之,恐怕你就只能得到无尽的嘲讽了。

 

技术细节

既然整体架构已经无甚可取之处,那么X框架的实现是否又像它所宣称的那样,能够解决诸多问题呢?既然X框架号称是“AOP/IoC的实现”,我们就选中这两项技术,看看它们在X框架中的实现和应用情况。

IoC

X框架宣称自己是一个“基于IoC的应用框架”。按照定义,框架本身就具有“业务代码不调用框架,框架调用业务代码”的特性,因此从广义上来说,所有的框架必然是基于IoC模式的。所以,在框架这里,“基于IoC”通常是特指“对象依赖关系的管理和组装基于IoC”,也就是Martin Fowler所说的Dependency Injection模式[2]:由容器统一管理组件的创建和组装,组件本身不包含依赖查找的逻辑。那么,X框架实现IoC的情况又如何呢?

我们很快找到了ContainerWrapper这个接口,其中指定了一个POJO容器核心应该具备的主要功能:

public interface ContainerWrapper {

  public void registerChild(String name);

  public void register(String name, Class className);

  public void register(String name, Class className, Parameter[] parameters);

  public void register(String name, Object instance);

  public void start();

  public void stop();

  public Collection getAllInstances();

  public Object lookup(String name);

}

在这个接口的默认实现DefaultContainerWrapper中,这些功能被转发给PicoContainer的对应方法。也就是说,X框架本身并没有实现组件容器的功能,这部分功能将被转发给其他的IoC组件容器(例如PicoContainer、Spring或HiveMind等)来实现。在ContainerWrapper接口的注释中,我们看到了一句颇可玩味的话:

/**

 * 封装了Container,解耦具体应用系统和PicoContainer关系。

了解IoC容器的读者应该知道,在使用PicoContainer或Spring等容器时,绝大多数POJO组件并不需要对容器有任何依赖:它们只需要是最普通的JavaBean,只需要实现自己的业务接口。既然对容器没有依赖,自然也不需要“解耦”。至于极少数需要获得生命周期回调、因此不得不依赖容器的组件,让它们依赖PicoContainer和依赖X框架难道有什么区别吗?更何况,PicoContainer是一个比X框架更成熟、更流行的框架,为什么用户应该选择X框架这么一个不那么成熟、不那么流行的框架夹在中间来“解耦”呢?

不管怎么说,至少我们可以看到:X框架提供了组件容器的核心功能。那么,IoC(或者说,Dependency Injection)在X框架中的应用又怎么样呢?众所周知,引入IoC容器的目标就是要消除应用程序中泛滥的工厂(包括Service Locator),由容器统一管理组件的创建和组装。遗憾的是,不论在框架内部还是在示例应用中,我们仍然看到了大量的工厂和Service Locator。例如作者引以为傲的缓存部分,具体的缓存策略(即Cache接口的实现对象)就是由CacheFactory负责创建的,并且使用的实现类还是硬编码在工厂内部:

  public  CacheFactory() {

cache = new LRUCache();

也就是说,如果用户需要改变缓存策略,就必须修改CacheFactory的源代码??请注意,这是一个X框架内部的类,用户不应该、也没有能力去修改它。换句话说,用户实际上根本无法改变缓存策略。既然如此,那这个CacheFactory又有什么用呢?

开源必读:开放-封闭原则

开源软件应该遵守开放-封闭原则(Open-Close Principle,OCP):对扩展开放,对修改封闭。如果你希望为用户提供任何灵活性,必须让用户以扩展(例如派生子类或配置文件)的方式使用,不能要求(甚至不能允许)用户修改源代码。如果一项灵活性必须通过修改源码才能获得,那么它对于用户就毫无意义。

 

在示例应用中,我们同样没有看到IoC的身影。例如JdbcDAO需要使用数据源(即DataSource对象),它就在构造子中通过Service Locator主动获取这个对象:

  public JdbcDAO() {

      ServiceLocator sl = new ServiceLocator();

      dataSource = (DataSource) sl.getDataSource(JNDINames.DATASOURCE);

同样的情况也出现在JdbcDAO的使用者那里。也就是说,虽然X框架提供了组件容器的功能,却没有(至少是目前没有)利用它的依赖注入能力,仅仅把它作为一个“大工厂”来使用。这是对IoC容器的一种典型的误用:用这种方式使用容器,不仅没有获得“自动管理依赖关系”的能力,而且也失去了普通Service Locator“强类型检查”的优点,又是一个“取其糟粕、去其精华”的设计。

开源必读:了解你自己

当你决定要在开源软件中使用某项技术时,请确定你了解它的利弊和用法。如果仅仅为了给自己的软件贴上“基于xx技术”的标签而使用一种自己不熟悉的技术,往往只会给你的项目带来负面的影响。

 

AOP

在X框架的源码包中,我们找到了符合AOP-Alliance API的一些拦截器,例如用于实现缓存的CacheInterceptor。尽管??毫不意外地??没有找到如何将这些拦截器织入(weave in)的逻辑或配置文件,但我们毕竟可以相信:这里的确有AOP的身影。可是,甫一深入这个“基于AOP的缓存机制”内部,笔者却又发现了更多的问题。

单从CacheInterceptor的实现来看,这是一个最简单、也最常见的缓存拦截器。它拦截所有业务方法的调用,并针对每次方法调用执行下列逻辑:

    IF 需要缓存

       key = (根据方法签名生成key);

       IF (cache.get(key) == null)

           value = (实际调用被拦截方法);

            cache.put(key, value);

       RETURN (cache.get(key));

    ELSE

       RETURN (实际调用被拦截方法);

看上去很好,基于AOP的缓存实现就应该这么做……可是,清除缓存的逻辑在哪里?如果我们把业务方法分为“读方法”和“写方法”两种,那么这个拦截器实际上只照顾了“读方法”的情况。而“写方法”被调用时会改变业务对象的状态,因此必须将其操作的业务对象从缓存中清除出去,但这部分逻辑在CacheInterceptor中压根不见踪影。如果缓存内容不能及时清理的话,用户从缓存中取出的信息岂不是完全错误的吗?

被惊出一身冷汗之后,笔者好歹还是从几个Struts action(也就是调用POJO Fa?ade的client代码)中找到了清除缓存的逻辑。原来X框架所谓“基于AOP的缓存机制”只实现了一条腿:“把数据放入缓存”和“从缓存中取数据”的逻辑确实用拦截器实现了,但“如何清除失效数据”的逻辑还得散布在所有的客户代码中。AOP原本就是为了把缓存这类横切性(crosscutting)的基础设施逻辑集中到一个模块管理,像X框架的这个缓存实现,不仅横切性的代码仍然四下散布,连缓存逻辑的相关性和概念完整性都被打破了,岂不是弄巧成拙么?

开源必读:言而有信

如果你在宣传词中承诺了一项特性,请务必在你的软件中完整地实现它。不要仅仅提供一个半吊子的实现,更不要让你的任何承诺放空。如果你没有把握做好一件事,就不要承诺它。不仅对于开源软件,对于任何软件开发,这都是应该记住的原则。

 

更有趣的是,X框架的作者要求领域模型对象继承Model基类,并声称这是为了缓存的需要??事实也的确如此:CacheInterceptor只能处理Model的子对象。但只要对缓存部分的实现稍加分析就会发现,这一要求完全是作者凭空加上的:用于缓存对象的Cache接口允许放入任何Object;而Model尽管提供了setModified()、setCacheable()等用于管理缓存逻辑的方法,却没有任何代码调用它们。换句话说,即便我们修改CacheInterceptor,使其可以缓存任何Object,对X框架目前的功能也不会有任何影响。既然如此,又为什么要给用户凭空加上这一层限制呢?

退一万步说,即使我们认为X框架今后会用Model的方法来管理缓存逻辑,这个限制仍然是理由不足的。毕竟,目前X框架还仅仅提供了缓存这一项基础设施(infrastructure)而已。如果所有基础设施都用“继承一个基类”的套路来实现,当它真正提供企业级应用所需的所有基础设施时,Model类岂不是要变得硕大无朋?用户的领域对象岂不是再也无法移植到这个框架之外?况且,“由领域对象判断自己是否需要缓存”的思路本身也是错误的:如果不仅要缓存领域对象,还要缓存String、Integer等简单对象,该怎么办?如果同一个领域对象在不同的方法中需要不同的缓存策略,又该怎么办?X框架的设计让领域对象背负了太多的责任,而这些责任原本应该是通过AOP转移到aspect中的。在X框架这里,AOP根本没有发挥它应有的效用。

开源必读:避免绑定

开源软件(尤其是框架类软件)应该尽量避免对你的用户造成绑定。能够在POJO上实现的功能,就不要强迫用户实现你的接口;能够通过接口实现的功能,就不要强迫用户继承你的基类。尤其是Java语言只允许单根继承,一旦要求用户的类继承框架基类,那么前者就无法再继承其他任何基类,这是一种非常严重的绑定,不论用户和框架设计者都应当极力避免。

 

写在最后

看完这篇多少有些尖刻的批评,恐怕读者难免要怪责我“不厚道”??毕竟,糟糕的开源软件堪比恒河沙数,为什么偏要选中X框架大加挞伐呢?在此,我要给各位读者、各位有志于开源的程序员一个最后、却是最重要的建议:

开源必读:切忌好大喜功

开源是一件长期而艰巨的工作,对于只能用业余时间参与的我们更是如此。做开源务必脚踏实地,做出产品首先在小圈子里内部讨论,然后逐渐扩大宣传的圈子。切勿吹大牛、放卫星,把“未来的愿景”当作“今天的承诺”来说??因为一旦工作忙起来,谁都不敢保证这个愿景到哪天才能实现。

国人还有个爱好:凡事喜欢赶个年节“献礼”,或是给自己绑上个“民族软件”的旗号,这更是开源的大忌。凡是做过政府项目的程序员,想必都对“国庆献礼”、“新年献礼”之类事情烦不胜烦,轮到自己做开源项目时,又何苦把自己套进这个怪圈里呢?当然,如果你的开源项目原本就是做给某些官老爷看的,那又另当别论。

 

所以,我的这位朋友怕也不能怪我刻薄:要不是他紧赶着拿出个远未完善的版本“新年献礼”,要不是他提前放出“AOP/IoC”的卫星,要不是他妄称这个框架“代表民族软件水平”,或许我还会夸他的代码颇有可看之处呢。有一句大家都熟悉的老话,笔者私以为所有投身开源者颇可借鉴,在此与诸位共勉:

                     长得丑不是你的错……

 

 

jjj提出的文档规范(转自jjj)

对于文档的设计,我觉得必须有一个普遍的规范,并且这个规范必须可以对项目的开发起到好的效果!

所以我认为(个人观点,仅限于开发部分的文档,至于需求部分大家可以共同讨论)

1.数据库的设计(或者说PO持久化层的设计),必须有一个明确的文档,这个文档中描述了各个表之间的关系

。每个属性对其他表中的数据是怎样影响的,对应关系是什么样的。比如说:一个属性对应另外一个表,并且

是一对多关系,我们就可以明显的看出通过这个属性获得的是一个List;如果是一对一关系,就可以知道,他们

共用一个ID,通过这个实体就可以获得另外一个实体(DAO.load("***Id));如果是多对一关系,我们就要考虑

在编写EJB的时候是否需要通过懒加载(Lazy

)的方法获得这个po。

2.Action和ActonForm的文档

 

必须重视他们,虽然他们有时看来很简单,如果可以有一个详尽的文档,对开发action来说很有帮助。

  我举个我们写的文档 的例子吧:

  2.1概述

     在**详细信息页面(**Detail.jsp)上的保存按钮所调用的Action。

    2.2数据session传输方式

   从session获取数据列表:

   获取名称   参数类型    参数说明

    **Form    **Form **Form

    2.3 属性

无。

     4.4 数据request传输方式

     从request获取数据列表:

     获取名称 参数类型 参数说明

      在request存入数据列表:

      存入名称   参数类型 参数说明

     4.5调用ejb的接口描述

      接口名称:ejb.**.**. **

      方法名称:public void save**(PO  po,List  **,List        **)throws

DataOperationException,RemoteException

        方法说明:保存**单和**电路;

      返回值:void

     4.6 异常处理

      异常:DataOperationException

      错误号:处理异常时与输出信息相对应的错误编号

      说明:数据操作异常

      跳转路径:error(所有的出错信息都跳转到这个页面)

     4.7 功能描述

     

**详细信息页面(**Detail.jsp)上的保存按钮所调用的Action,将修改后的受理单保存。

     4.8 逻辑实现

      1.获取**Form;

     

2.从**Form中提取出**单po信息,和需要删除的电路列表deleteList和要保存的列表saveList;

      3.调用ejb方法public void save**(PO  po,List  save**List,List 

**List)throws

DataOperationException,RemoteException,来保存受理单、接入电路;

       4.如果保存成功则删除**Form;

      5.如果成功,则forward=success, page=**Success.jsp;

      6.否则,转向错误页面**Error.jsp

希望大家写代码一定要参照代码规范(转自kula)

1. Java 命名约定

除了以下几个特例之外,命名时应始终采用完整的英文描述符。此外,一般应采用小写字母,但类名、接口名以及任何非初始单词的第一个字母要大写。

1.1 一般概念

n 尽量使用完整的英文描述符

n 采用适用于相关领域的术语

n 采用大小写混合使名字可读

n 尽量少用缩写,但如果用了,要明智地使用,且在整个工程中统一

n 避免使用长的名字(小于 15 个字母是个好主意)

n 避免使用类似的名字,或者仅仅是大小写不同的名字

n 避免使用下划线(除静态常量等)

1.2 示范

包(Package) 采用完整的英文描述符,应该都是由小写字母组成。对于全局包,将你的 Internet 域名反转并接上包名。 java.awt,com.learndiary.www.persistence

类(Class) 采用完整的英文描述符,所有单词的第一个字母大写。 Customer, SavingsAccount

接口(Interface) 采用完整的英文描述符说明接口封装,所有单词的第一个字母大写。习惯上,名字后面加上后缀 able, ible 或者 er,但这不是必需的。 Contactable,Prompter

组件/部件(Component) 使用完整的英文描述来说明组件的用途,末端应接上组件类型。 okButton, customerList,fileMenu

异常(Exception) 通常采用字母 e 表示异常。 e

类变量 字段采用完整的英文描述,第一个字母小写,任何中间单词的首字母大写。 firstName, lastName

实参/参数 同字段/属性的命名规则 public void setFirstName(String firstName){ this.firstName = firstName;}

局部变量 同字段/属性的命名规则

获取成员函数 被访问字段名的前面加上前缀 get。 getFirstName(), getLastName()

布尔型的获取成员函数 所有的布尔型获取函数必须用单词 is 做前缀。 isPersistent(), isString()

设置成员函数 被访问字段名的前面加上前缀 set。 setFirstName(), setLastName(),setWarpSpeed()

普通成员函数 采用完整的英文描述说明成员函数功能,第一个单词尽可能采用一个生动的动词,第一个字母小写。 openFile(), addAccount()

静态常量字段(static final) 全部采用大写字母,单词之间用下划线分隔。 MIN_BALANCE, DEFAULT_DATE

循环计数器 通常采用字母 i,j,k 或者 counter 都可以接受。 i, j, k, counter

数组 数组应该总是用下面的方式来命名:objectType[]。 byte[] buffer;

2. Java 注释约定

一个很好的可遵循的有关注释的经验法则是:问问你自己,你如果从未见过这段代码,要在合理的时间内有效地明白这段代码,你需要哪些信息。

2.1. 一般概念

n 注释应该增加代码的清晰度

n 保持注释的简洁

n 在写代码之前写注释

n 注释出为什么做了一些事,而不仅仅是做了什么

2.2. 示范

文档注释 在紧靠接口、类、成员函数和字段声明的前面注释它们。 /** 客户:客户是我们将服务和产品卖给的人或机构。*/

C 语言风格 采用 C 语言风格的注释去掉不再使用但你仍想保留的代码。仍想保留是因为用户万一会改变想法,或者在调试过程中想让它暂时失效。 /* 这部分代码因为已被它之前的代码取代,由 B.Gustafsson, 于 1999 年 6 月 4 日注释掉。如果两年之后还未使用,将其删除。. . . (源代码)*/

单行 在成员函数内采用单行注释,来说明业务逻辑、代码段和暂时变量的声明。注释符"//"后必须紧跟一个空格,然后才是注释信息。 // 遵照 Sarek 的规定,给所有// 超过 $1000 的发货单// 打 5% 的折扣。让利活// 动于 1995年 2 月开始.

2.3. 注释哪些部分

类 类的目的、即类所完成的功能,注释出采用的不变量。

接口 设置接口的目的、它应如何被使用以及如何不被使用。

成员函数注释 对于设置与获取成员函数,在成员变量已有说明的情况下,可以不加注释;普通成员函数要求说明完成什么功能,参数含义是什么返回什么;

普通成员函数内部注释 控制结构,代码做了些什么以及为什么这样做,处理顺序等。

实参/参数 参数含义、及其它任何约束或前提条件

字段/属性 字段描述

局部变量 无特别意义的情况下不加注释

3. Java 文件样式约定

所有的 Java(*.java) 文件都必须遵守如下的样式规则:

1) 版权信息

版权信息必须在 java 文件的开头,比如:

/** * Copyright ? 2000 Shanghai XXX Co. Ltd. * All right reserved. */

其他不需要出现在 javadoc 的信息也可以包含在这里。

2) Package/Imports

package 行要在 import 行之前,import 中标准的包名要在本地的包名之前,而且按照字母顺序排列。如果 import 行中包含了同一个包中的不同子目录,则应该用 * 来处理。

package hotlava.net.stats;import java.io.*;import java.util.Observable;import hotlava.util.Application;

这里 java.io.* 是用来代替InputStream and OutputStream 的。

3) Class

接下来的是类的注释,一般是用来解释类的。

/** * A class representing a set of packet and byte counters * It is observable to allow it to be watched, but only * reports changes when the current set is complete */

接下来是类定义,包含了在不同的行的 extends 和 implements

public class CounterSet extends Observable implements Cloneable{……}

4) Class Fields

接下来是类的成员变量:

/** * Packet counters */protected int[] packets;

public 的成员变量必须生成文档(JavaDoc)。proceted、private和 package 定义的成员变量如果名字含义明确的话,可以没有注释。

5) 存取方法(类的设置与获取成员函数)

接下来是类变量的存取的方法。它只是简单的用来将类的变量赋值获取值的话,可以简单的写在一行上,如类的成员变量已经有注释,类变量的存取方法可以没有注释。

public int[] getPackets() { return this.packets; }public void setPackets(int[] packets) { this.packets = packets; }……

要求说明的是,对于集合,加入成员函数来插入和删除项;另其它的方法不要写在一行上。

6) 构造函数

接下来是构造函数,它应该用递增的方式写(比如:参数多的写在后面)。

public CounterSet(int size){ this.size = size;}

7) 克隆方法

如果这个类是可以被克隆的,那么下一步就是 clone 方法:

public Object clone() { try { …… }catch(CloneNotSupportedException e) { …… }}

8) 类方法 (类的普通成员函数)

下面开始写类的方法:

/** * Set the packet counters * param r1 - …… * param r2 - …… * …… */protected final void setArray(int[] r1, int[] r2, int[] r3, int[] r4) throws IllegalArgumentException{ // Ensure the arrays are of equal size ……}

9) toString 方法

一般情况下,每一个类都应该定义 toString 方法:

public String toString() { ……}

10) main 方法

普通类,考虑置入一个main()方法,其中包含用于测试那个类的代码,如果包含了main() 方法, 那么它应该写在类的底部。

4. Java编码其它约定

n 文档化

必须用 javadoc 来为类生成文档。不仅因为它是标准,这也是被各种 java 编译器都认可的方法。使用 @author 标记是不被推荐的,因为代码不应该是被个人拥有的。

n 缩进

缩进应该是每行2个空格。 不要在源文件中保存Tab字符, 在使用不同的源代码管理工具时Tab字符将因为用户设置的不同而扩展为不同的宽度。

如果你使用 UltrEdit 作为你的 Java 源代码编辑器的话,你可以通过如下操作来禁止保存Tab字符, 方法是通过 UltrEdit中先设定 Tab 使用的长度室2个空格,然后用 Format|Tabs to Spaces 菜单将 Tab 转换为空格。

n 页宽

页宽应该设置为80字符。 源代码一般不会超过这个宽度, 并导致无法完整显示, 但这一设置也可以灵活调整。 在任何情况下, 超长的语句应该在一个逗号或者一个操作符后折行。 一条语句折行后, 应该比原来的语句再缩进2个字符。

n {} 对

{} 中的语句应该单独作为一行。 例如, 下面的第1行是错误的, 第2行是正确的:

if (i>0) { i ++ }; // 错误, { 和 } 在同一行 if (i>0) { i ++ }; // 正确, 单独作为一行

n 括号

左括号和后一个字符之间不应该出现空格; 同样, 右括号和前一个字符之间也不应该出现空格。 下面的例子说明括号和空格的错误及正确使用:

CallProc( AParameter ); // 错误

CallProc(AParameter); // 正确

不要在语句中使用无意义的括号,括号只应该为达到某种目的而出现在源代码中。

n JSP文件命名

采用完整的英文描述说明JSP所完成的功能,尽可能包括一个生动的动词,第一个字母小写,如:viewMessage.jsp、editUser.jsp或者forumChooser.jsp等。

n Servlet类命名

一般对应于所服务的对象加后缀Service来命名,如:UserService,TradeService等。

5. 一些编程建议

n 使用 StringBuffer 对象

在处理 String 的时候要尽量使用 StringBuffer 类,StringBuffer 类是构成 String 类的基础。String 类将 StringBuffer 类封装了起来,(以花费更多时间为代价)为开发人员提供了一个安全的接口。当我们在构造字符串的时候,我们应该用 StringBuffer 来实现大部分的工作,当工作完成后将 StringBuffer 对象再转换为需要的 String 对象。比如:如果有一个字符串必须不断地在其后添加许多字符来完成构造,那么我们应该使用 StringBuffer 对象和它的 append() 方法。如果我们用 String 对象代替 StringBuffer 对象的话,会花费许多不必要的创建和释放对象的 CPU 时间。

n 避免太多的使用 synchronized 关键字

避免不必要的使用关键字 synchronized,应该在必要的时候再使用它,这是一个避免死锁的好方法。 必须使用时,也尽量控制范围,最好在块级控制。

n 避免使用 java.util.Vector 类

因为"Unlike the new collection implementations, Vector is synchronized.",所以使用java.util.Vector类在性能上会有所减低。

n 尽量使用接口而不是一个具体的类

比方如下需求,给定一个SQL语句,返回一个对象的列表,实现中用java.util.ArrayList实现,于是定义方法为:

public java.util.ArrayList getObjectItems(String sql)

上面的方法存在一个问题,当getObjectItems内改用Vector或LinkedList实现,外部类必须做相应更改。一个更好的方法是定义返回值为java.util.AbstractList更合适:

public java.util.AbstractList getObjectItems(String sql)

这样即使更改实现,外部类也不必做相应更改。

n 避免使用索引来调用数据库中间层组件返回的结果集

如:

for(int i=1; i<=dt.getRowCount(); i++){ String field1 = dt.getField(i, 0).toString(); ……}

而应用字段名来存取结果集:

for(int i=1; i<=dt.getRowCount(); i++){ String field1 = dt.getField(i, "field1").toString(); ……}

这样在数据库设计更改或查询的SQL语句发生变化时,不会影响到程序的执行。

n 一行不能超过80个字

n 函数能写一行的话就写一行.比如 public bealean isLearn(){ return false; }

n 除非上面的情况.括号单独占一行.

//code example

package querysms;

import java.util.*;

import java.text.*;

public class test

{

    public static void main(String[] argv)

    {

        GregorianCalendar d1=new GregorianCalendar(1986,04,05);

        GregorianCalendar d2=new GregorianCalendar();

        Calendar d3=Calendar.getInstance();

        System.out.println("It was then"+d1.getTime());

        System.out.println("It is now"+d2.getTime());

        System.out.println("It is now"+d3.getTime());

        d3.set(Calendar.YEAR,1915);

        d3.set(Calendar.MONTH,Calendar.APRIL);

        System.out.println("D3 set to "+d3.getTime());

    }

}

恩。沉静了这么长时间。我来拉。。。

丛所周知。文档在软件开发中的重要性。在一些外包公司,文档被当成头等重要的大事来抓,文档能够很有效的帮助人们沟通。

它好比是一个交流的工具。尤其是象我们这种人员流动性极大的开发团队,文挡的重要性就更大了。简直可以影响整个团队的开发。

所以,我希望大家在写程序的时候。每写完一个类,一个文件。就一定要有一份相应的文挡发到文挡维护区来。一个文件对应一个文挡。文挡解释文件里函数的作用。属性的定义。还有为什么要有这么几个函数。这么几个属性。当时设计类为什么是这样设计而不是那样设计的。有什么考虑在里面。都可以写在文挡上。当你的文挡越详细,你就给团队留下了一份宝贵的财产,一份经验的积累。还有一份可读性良好的代码。

恩。顺便作下广告。有谁志愿加入文挡维护队的。可以跟我发邮件。现在进入还有神秘大奖哦~~嘿嘿嘿嘿