有经验的Java开发者和架构师容易犯的10个错误(下)

首先允许我们问一个严肃的问题?为什么Java初学者能够方便的从网上找到相对应的开发建议呢?每当我去网上搜索想要的建议的时候,我总是能发现一大堆是关于基本入门的教程、书籍以及资源。同样也发现网上到处充斥着从宽泛的角度描述一个大型的企业级项目:如何扩展你的架构,使用消息总线,如何与数据库互联,UML图表使用以及其它高层次的信息。

这时问题就来了:我们这些有经验的(专业的)Java开发者如何找到合适的开发建议呢?现在,这就是所谓的灰色区域,当然同样的也很难找到哪些是针对于资深开发者、团队领导者以及初级架构师的开发建议。你会发现网上那些纷杂的信息往往只关注于开发世界的一角,要么是极致(甚至可以说变态级别)地关心开发代码的细节,要么是泛泛而谈架构理念。这种拙劣的模仿需要有一个终结。

说了半天,大家可能明白我希望提供的是那些好的经验、有思考的代码、和一些可以帮助从中级到资深开发者的建议。本文记录了在我职业生涯里发现的那些有经验的开发者最常犯的10个问题。发生这些问题大多是对于信息的理解错误和没有特别注意,而且避免这些问题是很容易的。

让我们开始逐个讨论这些你可能不是很容易注意的问题。我之所以会用倒序是因为第一个问题给我带来了最大的困扰。但所有这10个问题(考虑一些额外的因素)对于你而言来说都有可能给你造成困扰(信不信由你);-)。

文章分上篇下篇,本文是下篇。

5、单例模式-已经成为反模式之一

译注:汗颜,我很难相信这个结论,不知道这句话的出处。

我已经在上面设计模式的讨论中提到了这一点,但单例模式需要单独说明。请跟着我重新下面的话:

单例模式是一个反模式 单例模式是一个反模式 单例模式是一个反模式 单例模式是一个反模式 单例模式是一个反模式……

单例模式以前有它们适用的场景。但是现在随着依赖注入框架的流行,单例模式已经可以完全剔除了。当你使用单例的时候,实际上正在向你的代码引入一系列新的问题。之所以这么说,理由如下:

1 单例模式在类中引入了隐含的依赖关系。

2 单例模式让代码变得不便于测试(即使用mock帮助)

3 单例模式把资源创建与资源获取给混在一起了

4 单例模式容易带来全局状态副作用(译注:这个实际上好理解,即单例实际上引入了全局状态,那么状态更改失败或者发生错误容易造成整个应用程序的行为不可确定)

5 单例模式容易带来并发问题。

如果你始终不相信我的话可以搜索一下,可以发现有一大堆的文章在讲述为什么单例模式是一个反模式。

译注:真的找到了许多文章

  1. http://Java.dzone.com/articles/singleton-design-pattern-%E2%80%93
  2.  http://stackoverflow.com/questions/11292109/why-is-singleton-considered-an-anti-pattern-in-Java-world-sometimes

4、忽略方法的可见性(作用域)

我一直羡慕那些有经验的Java开发者,他们认为Java只有3中方法可见性(作用域)修饰符。好吧,其实应该是四种,还有一个称为包缺省修饰符。但是这里我不想去解释这些事情,相反需要注意的是其它方面。

我真正想说的是,你需要特别注意公有方法。由于公有方法对整个程序来说都是可见的,所以这些方法需要尽可能足够简练,在编写类库时尤其如此(可以看一下SOLID准则)。

我很讨厌看见那些本应该是私有的方法被设计成公有方法。不仅仅因为这样对外界暴露了类的内部实现细节,更因为这些方法不能够也不应该被外界使用。

一个“经典”的结论是,你应该不停地对全局方法进行单元测试。我看到许多所谓的架构师相信把那些私有方法变成公有方法是可以接受的,仅仅因为这样做可以方便单元测试。

测试私有方法根本上就是错误的。正确的办法应该是通过测试公有方法来隐含地测试私有方法。需要提醒的是,如果只是为了单元测试,任何情况都不可以把类的私有方法变为公有方法(考虑使用方法覆盖method overriding)。

3、总是使用项目定制的StringUtils

这样的情况存在于一些早期的Java项目中,你会发现很多StringUtils、DateUtils、FileUtils等工具类。现在我开始了解这么做的问题,为了使这些工具类变得成熟稳定所付出的精力是巨大的。

但是新的代码在没有任何限制的情况下也这么做就变得不可理解了。一个架构师或者资深开发者的职责就是关注那些通过完整测试的解决方案,这些方案会更易于集成到你的应用程序。

如果你的职位描述包含架构师的字眼话,不应该有任何的理由说你不知道Apache Commons、Google Guava或者Joda Date。

你还需要在一些开发不熟悉领域的程序时提前做一些研究。例如,在写REST服务时候如果需要创建PDF文件,应该先花点时间了解在Java领域存在哪些REST框架以及创建一个PDF文件有什么推荐的方法(运行UNIX命令是不推荐的一种做法,Java可以通过iText自行创建PDF文件)。

2、构建过程依赖环境

我写Java程序已经10年了。告诉你一个秘密:构建一个企业级项目的时候只有一种可以接受的方式,那就是:

  1. 新人在公司出现的第一天(完成他自己相关的一些手续)。
  2. 新人熟悉工作环境(机器等),安装JDK和他所喜好的IDE(开发工具)。
  3. 新人从公司的代码仓库中拿出代码(使用SVN、Git或者其它工具)。
  4. 新人花费5分钟了解公司的代码是使用什么构建工具(例如:Maven、Gradle或者ANT)。
  5. 新人运行命令构建整个应用程序。

这不应该是一个理想化的状态,而应该是一种现实的状态去创建应用程序的构建系统(译注:顶,我也见过太多变态的构建系统了,最变态的系统我所碰到过为了构建系统需要运行20个命令,这还不包括发布的命令)。

如果你的应用程序依赖于一个特定IDE、特定版本的开发工具插件、个人电脑的本地文件、没有标注过的环境变量、网络资源或者其它任何非标准的资源。相信这时候需要重新思考你的构建系统了。

关于一步完成构建需要的信息可以参考 Joel test http://www.joelonsoftware.com/articles/fog0000000043.html

但请不要误解,一个需要自我检查的构建、额外的发布和生成文档的步骤、必要的外部设置或外部的数据库都需要在公司的wiki上有详细描述。

(对于一个新人来说)建议的构建行为应该至多1个小时内完成。我曾经看到有些项目,新人通常需要2天来完成第一次构建应用程序。

1、使用反射和自我检查

当你在编写ORM(对象关系映射)框架、Java代理、元编译器、IDE时,如果你愿意的话很有可能需要使用Java反射。然而大多数的企业级项目很大程度只是操作数据存储的一些CRUD接口(创建、读取、更新、删除),Java反射反而变成了一个杀手。

我经常看到Java反射被用作性能提升、向前兼容或向后兼容。而且几乎总是被错误地使用。反射总是基于一些现在成立但是将来未必成立的假设(例如,方法名称命名规则)。

反射的问题在于非常难于理解、调试以及解决相应的问题。

我甚至不愿意深入这种自我修正的代码。在Java企业级项目中,使用反射就相当于在一个大厦里面安装了一个定时炸弹。虽然这个大厦现在可能是稳定的,但只要到了那个时间所有将变成灰烬。

令我很困扰的是,那些“资深”的架构师一直反复介绍反射。在实际变成中,你可以通过清晰的对象层次、清晰的定义和文档、可插拔的架构完成同样的功能。

总结一下,不使用反射可以创建一个稳定、快速和可维护的Java应用程序。当然,如果想在企业级系统中增加麻烦的话那么就使用反射吧。

译注:实际上本人对Reflection,Annotation之类的技术,也有一些同样的想法。这里多加一些示例:

  1. 反射会创建许多匿名类,注意是类不是对象,会造成对于JVM的permanant generation(或old generation)空间占用。同时由于空间有限还会造成GC的发生,可能会加长系统响应延迟。
  2. 反射会提升性能?这是错误的观点。经过测试,反射一般会比正常的Java调用慢上1.X倍甚至10倍,所以很多时候需要缓存来加速。
  3. 反射有可能造成安全问题。譬如A.class.getMethod().setAccessbile(true),这样你可以访问私有类或者变量了)。

奖励环节:仅仅尝试优化你所真正掌握的实际上存在的瓶颈

译注:优化系统是一个经典悖论,没有瓶颈何须优化。

我经常看到架构师花费了巨大的精力在以下几个方面:

  1. 细粒度地调优log说明。
  2. 在旧的系统中替换Vector类。
  3. 替换String‘+’操作改为循环调用StringBuffer.Append()。
  4. 重构现有稳定的、成熟的、很少有bug发生的系统,仅仅是为了提升性能或者其他比较令人羡慕的目标。

悲观的说,很难评估这么做到底可以带来多大的收益和所需要花费的精力。即使系统运行地快了一些,但同时有可能带来其他更加严重的问题(像数据库死锁、内存泄漏、没有关闭打开的流)。很少有人会去关注这些问题。

花费时间解决logging可能只能提升了3%的性能,但改进SQL查询倒是有可能获得200%的性能提升。

因此,关于性能改进需要遵循下面四个重要步骤:

  1. 评估现在的系统。
  2. 应用你的更改。
  3. 再次评估修改过的系统。
  4. 衡量花费精力与提升性能之间的效能比。

请记住,优化的精髓在于—-评估而不是猜测。

总结

如果本文的一些话让你感觉有些敏感,那是因为我有过太多的帮助那些有经验的开发者清理垃圾代码的经历。而且解决上述这些问题远远比解决一个又一个有经验的高级工程师造成错误更痛苦。

期望在你解决类似的问题时上述10条可以用在你所擅长的领域,即使那些最好的开发者也需要记住总有些事情需要去学习和实践。

原文链接: zeroturnaround 翻译: ImportNew.com - Andy.Song
译文链接: http://www.importnew.com/7081.html
[ 转载请保留原文出处、译者和译文链接。]

关于作者: Andy.Song

10多年的架构设计,开发经验。 ( 新浪微博:@快乐小楠子 gitHub主页:https://github.com/hqxsn

查看Andy.Song的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

2 条评论

  1. aa 说道:

    替换String‘+’操作改为循环调用StringBuffer.Append()。 还有其他什么更好的办法???

    Thumb up 0 Thumb down 0

跳到底部
返回顶部