InfoQ

新闻

讨论:Java编程风格的改变

作者 赵劼 发布于 2009年8月17日 上午10时27分

社区
Java
主题
编程,
语言
标签
最佳实践,
Scala

最近Stephan Schmidt在博客中发表了题为《下一代Java编程风格》的文章,阐述了他眼中Java编程风格的改变,以及未来的走向:

许多公司和开发人员正在从Java转向其他编程语言:Ruby、Python、Grrovy、Erlang或Scala等等。不过你可能做不到这一点。即便如此,你也可以改变你的编程风格,获取这些新语言的优势。事实上,在过去的15年中,Java编程风格也已经有明显变化了。

Stephan在文章中提出了以下几点:

  1. 尽可能地标注final:让所有东西不可变,把变量标为final可以阻止改变它的值。很多时候,重新为变量赋值会引入bug,你应该使用新的变量。除此之外,final可以提高代码的可读性。我针对这个话题还写过一篇文章:《Java中所有变量都应该是final的
  2. 没有setter:许多Java程序员会自然而然地为类中所有的字段加上setter。思考一下,真的每个字段都需要修改吗?更好的方法是创建包含改变后状态的新对象。此外,也试着去除getter,我们应该遵循“Tell, don't ask”的思想。
  3. 避免使用循环来操作List:从函数式编程那里获得的经验,循环并不是进行集合操作最好方法。例如,我们可以使用Google Collections提供的过滤功能。
     
    Predicate canDrinkBeer = new Predicate() {
         public boolean apply(HasAge hasAge) {
             return hasAge.isOlderThan( 16 );
        }
    };
     
    List<Person> beerDrinkers = filter(persons, canDrinkBeer);
  4. 使用单行代码:Java是一门繁杂(noisy)的语言,我们应该编写更精确的代码。尝试将代码写为一行。例如:
     
    public int add(int a, int b) { return a + b; }
  5. 使用大量接口:领域驱动设计已经大行其道,一个应该拆分为多种“角色”,即实现多种接口,提高复用程度。方法应该面向“角色”,而不是面向特定的类。我在《不要在Java中使用String》一文中讨论了更多这方面的内容。
  6. 使用Erlang风格的并发:Java的并发特性(如lock和synchronized)过于低端,难以使用。Erlang风格的并发是一种更好的做法。Java平台上已经有了AkkaActorom。此外,也可以使用java.util.concurrent中的Join/Fork和数据结构进行编程。
  7. 使用Fluent Interface:Fluent Interface可以使代码更短,更容易编写。Google Collections中的MapMaker是个不错的示例:
     
    ConcurrentMap graphs = new MapMaker()
        .concurrencyLevel(32)
        .softKeys()
        .weakValues()
        .expiration(30, TimeUnit.MINUTES)
        .makeComputingMap(
            new Function() {
                public Graph apply(Key key) {
                    return createExpensiveGraph(key);
                }
            });
  8. 避免在DTO中创建getter和setter:如果你拥有简单的DTO(Data Transfer Object),不要耗费精力去编写getter和setter,直接使用公开的字段吧。不过在你无法完全控制代码的使用情况时,还是小心为上。

这篇文章发表之后,有许多人发表了不同的看法。其中Cedric Otaku发表了文章《下一代Java与现在差不多》予以回应,其中反对了Stephan提出的大部分观点。

  • 尽可能final:太多final会降低代码的可读性,它无法代码额外的好处。我已经不记得上次因为重新给变量赋值而造成错误是什么时候了。值得一提的是,在字段以外的成员上标记final违反了Google的风格指南。
  • 避免setter:看上去不错,不过这不现实。有些时候你不愿把所有的参数都通过构造函数传入。此外,如果使用对象池的时候,可变的对象会让编程更为方便。Stephan不是第一个提出要将访问器(accessor)从OO编程中移除的人,不过这个说法很明显不可行
  • 避免循环:Java并不适合函数式编程风格,所以我认为使用Predicate的代码反而难以读懂。我估计大部分的Java程序员会同意我的观点,即使他们已经熟悉了闭包风格。
  • 单行代码:这要视情况而定。并引入临时变量把一个表达式拆开可以提高代码可读性,也容易为其设置断点。
  • 使用接口:不错的建议,但也不能过火。过去我也争论过类似的话题,不过引入太多接口会导致细小类型的爆炸,使你高端的类型意图变得模糊。
  • Erlang风格并行:重申一点,使用Java设计以外的编程风格是危险的做法。java.util.concurrent中包含了非常有用的功能,我遇到过不少基于这些元素的Java抽象,它们要优于Erlang风格的actor架构。
  • Fluent Interface:这个建议比较有趣,它与Stephan提出的另一个建议“避免setter”相违背。Fluent Interface只是setter的另一种形式,不是吗?
  • 使用公有字段:不,千万别这么做。你不会因为加了访问器而后悔,但是我能保证你会因为一时偷懒,使用了公有字段而后悔莫及。

在Cedric的文章之后,Stephan又对他的说法进行了补充:

没有setter并不代表你不能修改这个对象,我只是说纯粹的setter不是面向对象的思维方式。例如,你觉得stop()和setStop(true)哪个更好一些?

(针对Predicate代码不易读)我认为你的假设有误。循环是“程序化”的代码,而Predicate是经过封装的,可以重用的,易于理解的“对象”。这里并没有函数式编程,这里是纯粹的OO - 我提起FP只是因为我从那里“引入”了这个方式。

还有许多人对Stephon和Cedric的文章发表了评论,例如有人支持Stephan的观点,认为final的可以更好的表示出代码的意图。甚至有人提出:

更简单的解决方案是使用Scala :) - 不可变的状态、统一访问原则(字段、属性、方法看上去一样)、单行代码、使用monads或函数来替代循环……这些特性都已经在Scala中优雅地体现出来了。

您的Java编程风格是什么样的,和过去相比有什么改变吗?

过犹不及 发表人 德翔 高 发表于 2009年8月17日 下午8时40分
有的很有道理 发表人 John Lee 发表于 2009年8月17日 下午9时11分
人性化 发表人 jian yang 发表于 2009年8月17日 下午11时37分
这种讨论很好。 发表人 zhao lei 发表于 2009年8月18日 下午7时58分
写得不错 发表人 easin chu 发表于 2009年8月19日 上午12时42分
有可取的地方 发表人 Yiding He 发表于 2009年8月19日 上午1时15分
Re: 有可取的地方 发表人 Jeffrey Zhao 发表于 2009年8月19日 上午4时8分
编程之道 发表人 sky lchao 发表于 2009年8月19日 上午11时27分
  1. 返回顶部

    过犹不及

    2009年8月17日 下午8时40分 发表人 德翔 高

    刻意的风格改变是不必要的。你总没法子从苹果里面吃出菠萝味道,尽管你可以把它刻成菠萝的样子。

  2. 返回顶部

    有的很有道理

    2009年8月17日 下午9时11分 发表人 John Lee

    例如没有语意的 getter/setter ,这已经误导很多人了
    另外,我不觉得Fluent Interface与取消多余setter有冲突,实际上我感觉完全不理解Cedric Otaku在这里的意思

  3. 返回顶部

    人性化

    2009年8月17日 下午11时37分 发表人 jian yang

    这个问题由来已久
    我觉得使用这些东西都需要程序员先思考,然后再使用
    即便你有最好的设计,但是程序员没有认真地去执行,还是白费心机

  4. 返回顶部

    这种讨论很好。

    2009年8月18日 下午7时58分 发表人 zhao lei

    Fluent Interface很强大。其他的好像也有借鉴之处。用final也值得考虑。不过一些控制的大对象,或状态频繁变化的大对象,用final岂不是要经常new大对象?

  5. 返回顶部

    写得不错

    2009年8月19日 上午12时42分 发表人 easin chu

    大部分意见和建议都不错。但是没有getter/setter 的变量在实际开发中是不现实的。

  6. 返回顶部

    有可取的地方

    2009年8月19日 上午1时15分 发表人 Yiding He

    我比较喜欢 Predicate。

  7. 返回顶部

    Re: 有可取的地方

    2009年8月19日 上午4时8分 发表人 Jeffrey Zhao

    Java的语法太累赘,真不适合函数式的风格。

  8. 返回顶部

    编程之道

    2009年8月19日 上午11时27分 发表人 sky lchao

    感觉编程是随意的,不要刻意的追求某种风格。只要能够从产品角度解决问题就可以了。

    对《编程之道》感触较深。

深度内容

基于Azure云计算平台的网格计算,第3部分:运行网格应用程序

在本系列的第1部分,我们介绍了在Azure上进行网格计算开发的设计模式,而在第二部分,我们用C#开发了一个网格应用程序。在这篇文章中,我们将来运行它。

文化基因生命周期

Julian Everett和Chris Matts认为:一个IT业务案例可以作为一种“文化基因(meme)”。它要在市场区隔这样的复杂生态系统中与其他文化基因——也就是其他IT业务案例 ——竞争。他们还揭示了其背后的理念。

基于VS2010的项目计划与跟踪

本演讲将探讨如何用Microsoft Visual Studio 2010搭配MSF for AgileScrum的流程模版,助力您的团队进行Agile项目的开发工作。本视频为第一部分,演示如何进行项目计划与跟踪。

在不改变语言的前提下如何推进Java的不断演进

在本文中,InfoQ谈到了有助于发掘潜在的新的Java语言特性的3种技术——DSL、注解处理器以及将语法糖从语言迁移到IDE中。

2010年大规模技术架构的思路

相比其他行业,IT技术由于信息流动便捷,新技术更新非常频繁。架构师经常面临新技术及传统方案选择的困惑。架构师应如何抓住本质构建新一代的应用?本文从几个方面提出一些思路供架构师参考。

功能小组模型的过程与质量控制

InfoQ中文站最近采访了微软的Ramesh,在采访中,Ramesh从过程控制、架构与设计的控制以及测试组织等方面分享了他所带领Visual Studio软件生命周期管理工具团队使用敏捷方式组织管理大规模软件团队方面的经验。

张文钿谈Rails开发及台湾Ruby社区

在去年10月份的Kungfurails大会上,InfoQ中文站有幸采访了从台湾专程赶过来的张文钿,与他探讨了关于台湾Ruby社区的发展、Rails的商业化,Restful Design等话题。

Scrum与策略

如果Scrum只关乎短期行为,战略人士又该如何去适应这种生态系统呢?更重要的是,它如何帮助企业领导人做出并且履行重要的承诺?