前车之覆,后车之鉴
——开源项目经验谈<!----><o:p></o:p>
(本文发表于《程序员》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”吗?
开源必读:便捷构建<o:p></o:p> 开源软件应该提供最便捷的构建方式,让用户可以只输入一条命令就完成整个项目的编译、构建和测试,并得到可运行的二进制程序。对于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的侵权。<o:p></o:p>
另外,作者打包用的压缩格式是RAR,而这个压缩格式对于商业用户是收费的。对于一个希望在商业项目中应用的框架项目来说,选择这样一个压缩格式实在算不得明智。而且笔者在源码包中还看到了好几个.jbx文件,这是JBuilder的项目描述文件。把这些JBuilder专用的文件放在源码包中,又怎能让那些买不起或是不想买JBuilder的用户放心呢?更何况,出于朋友的关心,笔者还不得不担心X框架的作者是否会收到Borland公司的律师信呢。
开源必读:授权先行<o:p></o:p> 在启动一个开源项目时,第一件大事就是要确定自己的授权协议,并在最醒目的地方用最正式的方式向所有人声明——当然,在此之前你必须首先了解各种开源授权协议。譬如说,GPL(Linux采用的授权协议)要求在软件之上的扩展和衍生也必须继承GPL,因此这种协议对软件的商业化应用很不友好;相反,APL则允许用户将软件的扩展产物私有化,便于商业应用,却不利于开发者社群的发展。作为一个开源项目的领导者,对于各种授权协议的利弊是不可不知的。<o:p></o:p> 除了源码本身的授权协议之外,软件需要使用的类库、IDE、解压工具等等都需要考虑授权问题。开源绝对不仅仅意味着“免费使用”,开源社群的人们有着更加强烈的版权意识和法律意识。如果你的开源软件会给用户带来潜在的法律麻烦,它离着被抛弃的命运也就不远了。 |
可以看到,不管从法律的角度还是从发布形式的角度,X框架都远够不上“Production/Stable”的水准——说实在的,以它的成熟度,顶多只能算是一个尚未计划周全的开源项目。虽然作者在自己的网站上大肆宣传,但作为一个潜在的用户,我不得不冷静地说:即便X框架的技术真的能够吸引我,但它远未成熟的项目形态决定了它根本无法在任何有实际意义的项目中运用。要让商业用户对它产生兴趣,作者需要做的工作还很多。
我刚才说“即便X框架的技术真的能够吸引我”,这算得上是一个合理的假设吗?下面,就让我们进入这个被作者寄予厚望的框架内部,看看它的技术水平吧。
整体架构
在X框架的宣传页面上,我们看到了这样的宣传词:
X框架解决了以往J2EE开发存在的诸多问题:EJB难用、J2EE层次复杂、DTO太乱、Struts绕人、缓存难做性能低等。X框架是Aop/Ico[注:应为“IoC”,此处疑似笔误]的实现,优异的缓存性能是其优点。<o:p></o:p>
下面是X框架的整体架构图:
可以看到,在作者推荐的架构中,EJB被作为业务逻辑实现的场所,而POJO被用于实现Façade。这是一个好的技术架构吗?笔者曾在一篇Blog中这样评价它:
让我们先回想一下,使用EJB的理由是什么?常见的答案有:可分布的业务对象;声明性的基础设施服务(例如事务管理)。那么,如果在EJB的上面再加上一 层POJO的Façade,显然你不能再使用EJB的基础设施了,因为完整的业务操作(也就是事务边界)将位于POJO Façade的方法这里,所以你必须重新——以声明性的方式——实现事务管理、安全性管理、remoting、缓存等基础设施服务。换句话说,你失去了 session bean的一半好处。另一方面,“可分布的业务对象”也不复存在,因为POJO本身是不能——像EJB那样——分布的,这样你又失去了session bean的另一半好处。<o:p></o:p>
继续回想,使用基于POJO的轻量级架构的理由是什么?常见的答案有:易于测试;便于移植;“开发-发布”周期短。而如果仅仅把POJO作为一层Façade,把业务逻辑放在下面的EJB,那么你仍然无法轻易地测试业务逻辑,移植自然也无从谈起了,并且每次修改EJB之后必须忍受漫长的发布周期。 即便是仅仅把EJB作为O/R mapping,而不是业务逻辑的居所,你最多只能通过DAO封装获得比较好的业务可测性,但“修改-发布”的周期仍然很长,因为仍然有entity bean存在。也就是说,即使是往最好的方面来说,这个架构至少损失了轻量级架构的一半优点。<o:p></o:p>
作为一个总结,X框架即便是在使用得最恰当的情况下,它仍然不具备轻量级架构的全部优点,至少会对小步前进的敏捷开发造成损害(因为EJB的存在),并且没有Spring框架已经实现的基础设施(例如事务管理、remoting 等),必须重新发明这些轮子;另一方面,它也不具备EJB的任何优点,EJB的声明性基础设施、可分布业务对象等能力它全都不能利用。因此,可以简单地总结说,X框架是一个这样的架构:它结合了EJB和轻量级架构两者各自的短处,却抛弃了两者各自的长处。<o:p></o:p>
在不得不使用EJB的时候,一种常见的架构模式是:用session bean作为Façade,用POJO实现可移植、可测试的业务逻辑。这种模式可以结合EJB和POJO两者的长处。而X框架推荐的架构模式,虽然乍看起来也是依葫芦画瓢,效果却恰恰相反,正可谓是“取其糟粕、去其精华”。
开源必读:架构必须正确<o:p></o:p> 在开源软件的初始阶段,功能可以不完善,代码可以不漂亮,但架构思路必须是正确的。即使你没有完美的实现,参与开源的其他人可以帮助你;但如果架构思路有严重失误,谁都帮不了你。从近两年容器项目的更迭就可以看出端倪:PicoContainer本身只有20个类、数百行代码,但它有清晰而优雅的架构,因此有很多人为它贡献外围的功能;Avalon容器尽管提供了完备的功能,但架构的落伍迫使Apache基金会只能将其全盘废弃。<o:p></o:p> 所以如果你有志于启动一个开源项目(尤其是框架性的项目),务必先把架构思路拿出来给整个社群讨论。只要大家都认可你的架构,你就有机会得到很多的帮助;反之,恐怕你就只能得到无尽的嘲讽了。 |
技术细节
既然整体架构已经无甚可取之处,那么X框架的实现是否又像它所宣称的那样,能够解决诸多问题呢?既然X框架号称是“AOP/IoC的实现”,我们就选中这两项技术,看看它们在X框架中的实现和应用情况。<o:p></o:p>
IoC
X框架宣称自己是一个“基于IoC的应用框架”。按照定义,框架本身就具有“业务代码不调用框架,框架调用业务代码”的特性,因此从广义上来说,所有的框架必然是基于IoC模式的。所以,在框架这里,“基于IoC”通常是特指“对象依赖关系的管理和组装基于IoC”,也就是Martin Fowler所说的Dependency Injection模式:由容器统一管理组件的创建和组装,组件本身不包含依赖查找的逻辑。那么,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又有什么用呢?
开源必读:开放-封闭原则<o:p></o:p> 开源软件应该遵守开放-封闭原则(Open-Close Principle,OCP):对扩展开放,对修改封闭。如果你希望为用户提供任何灵活性,必须让用户以扩展(例如派生子类或配置文件)的方式使用,不能要求(甚至不能允许)用户修改源代码。如果一项灵活性必须通过修改源码才能获得,那么它对于用户就毫无意义。<o:p></o:p> |
在示例应用中,我们同样没有看到IoC的身影。例如JdbcDAO需要使用数据源(即DataSource对象),它就在构造子中通过Service Locator主动获取这个对象:
public JdbcDAO() {
ServiceLocator sl = new ServiceLocator();
dataSource = (DataSource) sl.getDataSource(JNDINames.DATASOURCE);<o:p></o:p>
同样的情况也出现在JdbcDAO的使用者那里。也就是说,虽然X框架提供了组件容器的功能,却没有(至少是目前没有)利用它的依赖注入能力,仅仅把它作为一个“大工厂”来使用。这是对IoC容器的一种典型的误用:用这种方式使用容器,不仅没有获得“自动管理依赖关系”的能力,而且也失去了普通Service Locator“强类型检查”的优点,又是一个“取其糟粕、去其精华”的设计。
开源必读:了解你自己<o:p></o:p> 当你决定要在开源软件中使用某项技术时,请确定你了解它的利弊和用法。如果仅仅为了给自己的软件贴上“基于xx技术”的标签而使用一种自己不熟悉的技术,往往只会给你的项目带来负面的影响。 |
AOP
在X框架的源码包中,我们找到了符合AOP-Alliance API的一些拦截器,例如用于实现缓存的CacheInterceptor。尽管——毫不意外地——没有找到如何将这些拦截器织入(
分享到:
相关推荐
### 正确使用汽车“眼睛”——车灯的关键知识点 #### 一、车灯的重要性与种类 车灯作为汽车的“眼睛”,不仅具有装饰性,更重要的是保障行车安全。常见的车灯包括: - **夜行照明灯**:主要用于夜间行车照明。 - *...
车联网社交作为车联网发展的新起点,其概念的提出是为了解决现有车联网功能使用度和粘度不足的问题。车联网通过车辆通信技术实现车与车、车与路、车与人等信息交互,从而实现智能交通管理和车载信息服务。但随着功能...
标题中的“高速行车安全提示——安全驾驶快乐出行ppt模板.rar”表明这是一个关于高速行车安全的教育材料,以PPT的形式呈现,旨在帮助驾驶者了解如何在高速公路上安全驾驶,从而确保出行的安全与愉快。 描述中提到的...
实施“日出车前车容车貌检查”和“收车后安全检查”,并严格执行交接班制度,确保每一辆电动出租车在投入运营前都处于最佳状态。此外,每月的车辆点检和市客运处的月检相结合,进一步强化了对车辆状况的监控,防止...
当前车速度大于3m/s跟随前车行驶,当前车速度小于3m/s,对前车绕行
Apollo感知之旅——感知算法.pdf Apollo感知之旅——感知算法.pdf是一个关于自动驾驶感知算法的进阶课程,涵盖了雷达感知、激光雷达感知、视觉感知等领域。该课程首先介绍了自动驾驶感知系统的重要性,然后详细...
- 前车之覆,后车之鉴 - 两虎相斗,必有一伤 - 近朱者赤,近墨者黑 - 乘兴而来,败兴而归 - 萧何月下追韩信,败也萧何 - 焉知非福,塞翁失马 - 有志者,事竟成 - 鞠躬尽瘁,死而后已 - 绳锯木断,水滴石穿 ...
在驾驶过程中,正确判断与前车、后车以及左右两侧车辆的距离是安全驾驶的重要技能,尤其是在进行侧方停车等操作时更为关键。本篇将详细讲解如何通过观察和技巧来评估这些距离。 首先,判断与前车的距离主要依赖于...
3. **自适应巡航控制(ACC)**:ACC能在高速公路或畅通路段保持设定速度,同时根据前车距离自动调整速度,减轻驾驶疲劳。 4. **车道保持辅助(LKA)**:当车辆偏离车道线时,LKA会通过方向盘振动提醒驾驶员,必要时...
描述中提到的“前车之覆,后车之鉴”,强调了从他人的错误中吸取教训的重要性。案例中的主角由于价值观扭曲,抵挡不住诱惑,最终走向了犯罪道路,这警示我们,无论在何种情况下,都不能让贪婪和欲望凌驾于法律之上。...
《农电安全生产知识调考试题集——防交通事故类》文档主要涵盖了交通安全的多个方面,旨在强化农电行业的安全意识,防止交通事故的发生。以下是该文档涉及的主要知识点: 1. **交通安全管理体系**:要求建立健全...
总结起来,前车之覆,后车之鉴。每位员工都应将安全生产放在首位,通过学习事故案例,提高安全意识,及时发现并消除隐患。双电笔、断点确认、劳保用品的正确使用以及匹配的工具,都是保障安全的有效手段。只有全面...
“前车之覆,后车之鉴”,我们必须时刻牢记“安全第一”的理念。在建筑行业中,确保“安全、稳定、长期、满负荷、优质”不仅是我们的职责,更是对企业和工人的负责。不把安全放在首位,不仅无法实现高效优质的工程,...
车辆检测与前车碰撞预警是智能驾驶领域中的关键技术之一,主要应用于高级驾驶辅助系统(ADAS)。这一技术旨在提高行车安全,通过实时监测车辆周围的环境,预测潜在的危险情况,特别是与前方车辆的距离和相对速度,从而...
前车碰撞预警——FCW,基于深度学习和单目摄像头测距的前车碰撞预警源码。 单目测距,多目标跟踪。 车辆检测,智能adas,FCW,价格只包括源码及设计文档讲解。 我使用的版本说明: gpu版本: anoconda:3-5.1.0 cuda:...
前车碰撞预警——FCW,基于深度学习和单目摄像头测距的前车碰撞预警源码。 单目测距,多目标跟踪。 车辆检测,智能adas,FCW,价格只包括源码及设计文档讲解。 我使用的版本说明: gpu版本: anoconda:3-5.1.0 cuda:...
在传统的基于制动过程的安全距离模型的基础上,考虑了前后车之间的速度关系和车辆制动减速度的渐变过程,建立了单车道跟驰状态下车辆跟驰的安全距离模型。通过Matlab仿真计算,从理论上验证了该模型能够很好地解决...
车辆工程 途锐汽车前车雷达失效故障诊断与维修