当前位置导航:炫浪网>>网络学院>>编程开发>>JAVA教程>>Java入门

四个有害的Java编码习惯

    程序中的编码风格让我们的编程工作变得轻松,特别是程序维护员,他们要经常阅读其他人编写的程序编码,这一点尤其突出。编码规范从根本上解决了程序维护员的难题;规范的编码阅读和理解起来更容易,也可以快速的不费力气的借鉴别人的编码。对将来维护你编码的人来说,你的编码越优化,他们就越喜欢你的编码,理解起来也就越快。

    同样,高水平的编码风格(例如固定的封闭结构)目的在于改善设计和使编码更易于理解。事实上,最后有些人会认为改善设计和提高编码的易读性是一回事。

    本文中你会看到一些流行的编码风格被面向读者的更易于接受的风格所替代。有人争论说这些风格都已经被大家广泛使用,不应该简单的为了达到读者的期望而抛弃。然而,读者的期待只是其中一方面的原因,不可能凌驾于所有因素之上。列出四种常见的问题:

    1.对局域变量(local variables)、参数(method arguments)、字段(fields)这三种变量的命名没有区分:

    对看编码的人来说,首先要弄清这些数据如何定义的?看一个类时,得弄清楚每个条目是局域变量?字段?还是参数?有必要使用一个简单的命名约定来定义这些变量,增加易读性。

    很多权威机构规范过字段变量用以区分它与其它的变量,但这远远不够。可以把对字段的合理的命名约定逻辑也应用在参数上面。先看示例1:没有进行区分这三种变量的类定义,如下所示:

    示例1:

public boolean equals (Object arg) { 
if (! (arg instanceof Range)) return false;  
Range other = (Range) arg; 
return start.equals(other.start) && end.equals(other.end);}

    在这个方法中,arg直接用argument的缩写,虽然大家一看就知道这是参数了,但这种命名方式却丢失了参数代表的对象本身 的含义。大家知道这 是参数,却不知道这是什么参数。如果方法的参数多一点,都按照arg1,arg2这样的方式命名,阅读代码  的时候很头疼。另外两个字段变量,start和 end,突然凭空而出,想一下才知道这应该是字段。当然,这个方法很短,造成的困难还不大,如果这个方法比较长的话,突然看到start和end两个变 量,一般会先在前面找一下是不是局部变量,然后才能确定是类的字段变量。

    这个问题貌似微不足道,但为什么要让代码阅读者花费额外时间在这些琐碎的问题上呢?如果有个方案能让代码阅读者一目了然的明白变量是那种变量,为什 么不采用呢?就如同Steve McConnell在 《代码大全》中说的:"让人费神去琢磨神秘杀人凶手这没有问题,但你不需要琢磨程序代码,代码是用来阅读的。

    接下来看示例2,使用命名约定后对示例1重写以后的代码,用到的命名约定有:

    ◆参数定义时名字加前缀a

    ◆字段定义时名字加前缀f

    ◆局域变量定义时不加任何前缀

    示例2:对变量类型进行区分

public boolean equals (Object aOther) { 
 if (! (aOther instanceof Range)) return false; 
 Range other = (Range) aOther; 
 return fStart.equals(other.fStart) && fEnd.equals(other.fEnd);}

    你可能反对示例2中的风格,反对过时了的匈牙利符号,但是我认为反对是错误的,因为匈牙利符号能详细说明信息的类型。上面的命名约定区分了类型。而且这样做分清了字段、变量和局域变量,这是两种完全不同的概念。这种命名约定的方式并不像看起来那么微不足道:当这些约定用在程序编码中时,会大大降低理解的难度,因为你可以不需要先分辨这些变量,省去不少时间。

    2.按层次划分包,而不是根据特征或功能划分最常见的划分应用序就是按层次命名包:

    com.blah.action 、com.blah.dao 、com.blah.model、com.blah.util

    也就是说,把具有同样特征或者功能的类划分到了不同的包里。因为成员的属性对其他成员应该是可见的,这就意味着几乎应用程序中所有的类都是公共的。实际上,这种按层次划分包的方法完全扔掉了Java的包内私有。包内私有应该彻底不使用。现在,包内私有是Java程序语言中设计者的默认作用域。这种包的划分习惯也违反了面向对象编程的核心原则之--尽量保持私有以减少影响,因为这种习惯强迫你必须扩大类的作用域。由于一些奇怪的原因,一些Java组织不赞成这种命名,似乎不公正的。
另一种风格是按特征划分命名:

    com.blah.painting 、com.blah.buyer 、com.blah.seller 、com.blah.auction 、com.blah.webmaster 、com.blah.useraccess 、com.blah.util

    这里,成员不按行为划分,而是按照不同特征的类划分,每个成员都关联不同的特征。这种方法下包在最初使用是被定义。
例如:在Web应用程序中,“com.blah.painting”包可能由下列成员组成:

    ◆Painting.java: 一个model对象

    ◆PaintingDAO.java: 一个数据存取对象Dao

    ◆PaintingAction.java: 一个控制或者行为对象

    ◆statements.sql: Dao对象使用的SQl文件

    ◆view.jsp: Jsp文件

    需要特别说是的是,这种划分方法,每一个包都包含所有成员有关的特征文件,而不仅仅是Java源文件。这种按特征划分包的方法,要求在做删除操作时要注意,删除一个特征时要删掉它的整个目录,不能保存在源码中。

    这种方法优于按层次划分包的方法,表现在以下几点:

    ◆包是高内聚的,并且模块化,包与包之间的耦合性被降到最低。

    ◆代码的自描述性增强. 读者只需看包的名字就对程序有些什么功能或特征有了大概的印象。在《代码大全》中, Steve McConnell 将自描述性的代码比作 "易读的圣杯",来表达它的易读性。

    ◆把类按照每个特征和功能区分开可以很容易实现分层设计。

    ◆相关的成员在同一个位置。不需要为了编辑一个相关的成员而去浏览整个源码树。

    ◆成员的作用域默认是包内私有。只有当另外的包需要访问某个成员的时候,才把它修改为public. (需要注意的是修改一个类为public,并不意味着它的所有类成员都应该改为public。public成员和包内私有(package- private)成员是可以在同一个类里共存的。)

    ◆删除一个功能或特征只需要简单的删除一个文件夹。

    ◆每个包内一般只有很少的成员,这样包可以很自然的按照进化式发展。如果包慢慢变的太大,就可以再进行细分,把它重构为两个或者更多新的包,类似于物种进化。而按照层次划分的方式,就没办法进化式发展,重构也不容易。

    一些框架推荐使用层层定义包的传统的方式做为包的命名方法:由于使用传统的包命名,开发者总能知道在哪个位置可以找到这些项目,但是为什么避免人们这样做呢?使用另一种按特征定义包的风格,就不需要这种单调的操纵,因此,按特征定义完全超越了任何其它命名约定。约书亚布洛赫在《高效的Java》一书中说到:区分一个设计好坏的唯一重要因素是模块内部隐藏的数据和其它模块中涉及的实现过程的程度。

共2页 首页 上一页 1 2 下一页 尾页 跳转到
相关内容
赞助商链接