细数jdk库的四宗罪

标签: java反模式JDK设计缺陷
343人阅读 评论(0) 收藏 举报
分类:

写过java代码的编程人员都有这种感慨——jdk库非常庞大,并且有非常多精心设计的工具可以拿来用。另一方面,jdk版本的向下兼容性也做得非常好,升级版本对旧项目来说没多大困难。由于这两点原因,jdk不可避免地存在一些设计上的缺陷。为了保障旧项目开发团队的利益,这些设计缺陷只能随着众多版本被继承下来。

个人总结了一下,下面几点我们可以把它当作反面教材。


第一宗罪:破坏封装性,公有类直接暴露数据域

如果问一个程序员,为什么javabean不直接将属性设为public,而需要浪费那么多精力搞一堆getter/setter方法。很多人会回答——为了让代码看起来更加oop。

其实这个回答没有抓住重点。我们之所以要实现getter/setter方法,依我看来,至少有两点原因:1、允许我们在调用方法时作限制与记录,例如对setter的参数作边界的判断,在调用方法时增加日志或断点调试;2、可以保持对外开放接口不变的同时修改内部数据结构。

JDK库的几个类破坏了这种封闭性,比较典型的是awt包里的Dimension和Point两个类。从源代码可以看出,这两个类的属性域都直接是public修饰的。


第二宗罪:滥用继承

util包有一个工具类,明显是滥用继承的代表。没错,这个类就是鼎鼎大名的Stack类了。

我们知道Statck数据结构本身的特点就是“后进先出”。那么它理想就应该只有两个方法来操纵数据,一个是push()方法,另一个是pop()方法。但是由于继承了Vector,Stack从父亲那里继承了诸如add(),remove(),set()等违反游戏规则的方法。这简直无法直视!!


第三宗罪:值对象应该设计为不可变对象

不可变对象是指,实例属性经过初始化后在对象的整个生命周期内固定不变。例如jdk的String以及各种基本类型的包装类。

不可变对象易于设计,并且在并发环境下更加安全,无需额外的同步机制。

util包里的Date类属于值对象,类似于人民币一元,十元的概念,应该被设计为不可变对象。也就是说,不应该提供各种setter方法来修改对象的属性域。


第四宗罪:使用常量接口

常量接口是指没有任何方法,只包含表态final域的接口对象,每一个域都导出一个。实现该接口的类即可获得接口的所有常量属性,看起来非常方便!

然而,常量接口模式是对接口的不良使用。首先,常量的使用应属于内部实现细节,实现常量接口会把这样的细节暴露到导出的API中。其次,如果在后续升级中发现一个类不再需要以前的常量,这个类依然必须实现这个常量接口,以确保向下兼容性。

jdk平台io包里的ObjectStreamConstants就属于常量接口,不值得效仿。

那么,如果需要导出常量,最好的选择方案是jdk5引入的枚举类型(enum)。当然,如果某些常量跟某个类或接口联系非常紧密,也可以把这个常量绑定在该类,例如Integer.MAX__VALUE或Math.PI等。

然而,某些开发人员还是喜欢选择常量接口,因为枚举略显繁琐,而类又必须写一串长长的"public static final "修饰。但是他们的使用还是有底线的,当需要某个常量的,代码只会用XXConstants.XX的模式,而不会直接让类实现这个接口。这种使用算是一种折中方案。采用哪种方法,还是看项目规范吧。


考虑到jdk库的庞大以及兼容性,以上列出的几个“瑕疵”也就瑕不掩瑜,无可厚非了。jdk的源码还是非常值得我们好好学习与揣摩的。

1
0

猜你在找
【直播】机器学习&数据挖掘7周实训--韦玮
【套餐】系统集成项目管理工程师顺利通关--徐朋
【直播】3小时掌握Docker最佳实战-徐西宁
【套餐】机器学习系列套餐(算法+实战)--唐宇迪
【直播】计算机视觉原理及实战--屈教授
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之矩阵--黄博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之凸优化--马博士
【套餐】Javascript 设计模式实战--曾亮
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:101496次
    • 积分:1421
    • 等级:
    • 排名:千里之外
    • 原创:41篇
    • 转载:0篇
    • 译文:0篇
    • 评论:27条
    博客专栏
    最新评论