[译] Java里臭名远扬的代码:你认出来了吗?
八月份的第三个周日,写的第四篇文章,坚持写下去一定会有收获,正所谓,万事开头难、中间难、结尾难。。。
okay,本周在实际工作中遇到了蛮多的新知识,鉴于每日工作繁忙,周日还要加班,因此只有周六一天可以‘忙中作乐’,不过这个周六依然过得十分‘充足’,上午陪同事找房子忙到下午2点,下午三点去学习游泳学到六点钟。此外,本周抽时间重新布局了我的卧室,安装了一张写字桌,组装一台电脑,现在终于可以在周日的晚上安安静静的写点东西。本周决定翻译一篇dzone.com上面的一篇文章练习一下,下周决定写一篇介绍 Android Notification 的文章,巩固学习。
阅读本文大概需要 4.5 分钟
正文
这些违反设计原则和质量的代码结构简直就是臭虫。
下面我们就列举一些常见的臭虫代码。
常量接口(Constant Interface)
常量接口只包含了静态常量数据成员,没有声明任何的方法在里面。建议的重构方式要根据常量接口里面常亮的种类:常量是类里面的成员可以被添加或者是可以被写入的枚举。接口 java.io.ObjectStreamConstant 就是一个很好的例子。
全局变量类(Global Variable Class)
这种类拥有一个公共静态成员(非final),由于这种变量可以被任何人修改,这就变成了类似于 C 语言里面的全局变量,不同的是,在使用前要加上类的名字来防止变量名冲突。下面是一个例子:
class Balls {
public static long balls = 0;
}
全局方法类 (Global Function Class)
这是一种只有一个静态公共方法没有任何其他的成员或者方法的public修饰的类;他有一个可选的用private属性修饰的构造方法来禁止实例化。这相当于C语言里面的全局函数,除了在使用他时需要在前面加上类的名字。
公共裸露的成员 (Publicly Exposed Fields)
这是一种public修饰的,他的成员都是public修饰的,非final,非static,并且没有任何方法在里面(可能会有一个可选的构造方法),这是难以保持的public修饰、类似C的一些类,正如 Effective Java 中所写道的:“一些java平台库里面的类,忽视public类不应该直接暴露成员的建议。最突出的例子就是 java.awt 包里面的 Point 类和 Dimension 类,这些栗子不是简单的来说说,而是应当被视作严重警示。
孤立的抽象(Orphan Abstract)
这是一个抽象abstract 修饰的没有任何实现类的类,一个抽象类,只有当具体实现的时候,他才能获得‘人生价值’。如果一个抽象类没有任何用处,那么还留你何用(可以移除了)?如果这个类可以考虑实例化,那么就去具体实现;如果这代表一种有用的抽象,就提供若干具体实现类去实现他。
被遗忘的接口 (Forgotten Interface)
一个类用同样的签名实现了所有接口列举的方法。这很有可能是一种失误:类原本是想要实现一个接口,但是忘记列举接口作为他的基本类型。这种臭虫做法的后果就是:该类的对象不能被视作接口的子类,于是,子类型的好处和运行多态都不能被利用。
双胞胎类 (Clone Class)
一个类完完全全克隆另外一个类,不是继承关系。实质上,只是类的名字不太一样,但是他们的全部成员、签名其他等等都是一样的。约束他们的仅仅是至少有一个不同的成员。一个例外就是那个成员顺序可能不太一样。
如果有若干个类拥有共同的成员或者表现,他们应该继承一个共同的类,包含那些共同的数据和表现。因此, 一个解决方法就是看看有没有可能提供一个基类,并且让其他类成为他的派生类 ,克隆类常常发生在复制粘贴代码,在这种情况下,最好就是移除副本,用一个独一无二的类取代。
孤独的类 (Lonely Class)
这种类不依赖于或使用任何其他类,并且,任何其他的类也不依赖或者使用这个类。这也许并不是一个错误或者失误,但是,然而,很少能见到一个非常独立的类和其他的类没有任何的联系。因此,我相信这应该是一个设计错误。
抽象类在不重要的位置(Abstract Leaf)
(这个实在不知道怎么翻译)抽象类在继承树的根部时一般都有很大的意义和用处,但是在另外一种情况下,当抽象类在继承树的叶子时:通常代表无意义可以被淘汰的;通常都意味着设计失误。
标记类 (Tagged Class)
这种情况通常发生在那些出身结构编程背景的程序员写的程序。他们通常像定义结构体一样定义类,而不是提供继承相关类型和多态。他们通常用一系列枚举和switch case 语句,或者是一串长长的 is else 语句 来区分类型然后做相应的操作。
术语 ‘tag class’ 来自于 Effective Java :“这种类的实例有两个或者更多的‘口味’ 并且有一个标签域指明这种实例的‘口味’”。
通过以为几点来检查是不是 ‘tag class’ :
- 一个枚举(或者很多public 修饰的 static final 数据)来指出实例的flavour。
- 一个域来保存 enum/int 的值,典型的,通常在构造函数中类设置他。
- 一个switch语句在一个或者更多的方法里,基于tag来决定执行的代码。
很明显的,重构 tag class 的方法就是继承。
小结
根据你的经验,你认出上面的那些代码了吗?欢迎补充更多的类似常见的代码。
有很多的静态分析工具(FindBugs, PMD, etc) 可以用来分析检查java里面的bug模式,然而,并没有很多的工具用于专门分析这样的臭代码。。希望未来能很快出现这样的分析工具。
作者有话说
还说啥。。。已经零点了,明天还得早起搬砖,晚安。
附上原文链接