转:为什么getter / setter 方法是邪恶的?

我并不想开始一个“邪恶的”系列文章,但是一些读者要我解释为什么上个月的专栏中提及要避免使用 get/set方法,“为什么extends是邪恶的?”  虽然getter/setter方法在java里处处可见,但它们不是完全OO的,实际上,它们会破坏你代码的可维护性,而且,当你的程序并不需要一个从OO方面来讲很好的的设计时。大量存在的getter setter方法是一个危险的信号。
这篇文章解释为什么你不应当使用getters setters,什么情况下使用,并且给出一个帮助你打破getter/setter思维定势的设计方法。
上个月的专栏里,我很惊讶一些读者的评论,“为什么extends是邪恶的”,一些人认为我在说OO实在是过于简单因为extends存在很多问题,好像把二个概念等同起来了。这明显不是我的所想所言,所以我在这里澄清一些本来的观点。
这篇专栏和上月的文章是关于设计的,设计,很自然要抛弃一系列旧有的东西。每一个选择都有好的一面和坏的一面,你应根据需要做一个全面客观的权衡,好和坏都是相对的,毕竟,一个好的选择,从另一个角度来讲可能很不利。
如果你不明白一个论点的二面性,就不能做出明智的选择,实际上,如果你看不到行为的副作用,根本就不能做设计,只能在黑暗中前行。这个观点并不意外,在四人帮的的设计模式每一章中,都包含了一段“Consequences”来描述什么时候、为什么使用该模式,和什么情况下不适宜。
一些语言的特性或习惯编程用法存在问题,并不是说你就应在任何情况下都不用要用它们。或者,仅仅因为一些用法已经很常见,你就应该去用它们。不知情的程序员用会SunMS提供的东西写很多的程序,这不会提高他们的编程或设计能力。Java包里有大量的代码,但是我相信,还是有一部份,让他们的作者感到窘迫而想重新写的。
另外,市场或政策经常推动设计习惯。有时候,程序员会做出糟糕的决定,但是公司想要强调自己能达到的技术,所以不再说你做的这些不够理想,他们为这个糟糕的解决方案全力而为,因此,当你的程序实现都是因为“被期望这样做”,你是不负责的。很多失败的EJB项目验证了一点,基于EJB的技术被正确使用的时候非常棒。但是它经常用的不合适宜而造成一些公司的困境。
OO系统的一个基本原则是一个Object不应暴露它的实现细节,这样,当你改变了实现细节,就可以不用改变使用了该对像的的代码部份。所以,你应当避免getter 和 setter 函数,因为它们主要用来访问实现细节。
来看看为什么,假设你的程序中有1000处调用 getX() 方法,假设每个调用都返回一个特定类型,你可能把 getX() 的返回值存在一个局部变量中,那个变量类型必须匹配返回值类型,如果你需要改变对像的实现细节而改变了X的类型,你将陷入巨大的麻烦中。
如果X int 类型,但是现在必须变为long ,将得到1000个编译错误,如果你通过把返回值改为int而修正这个错误,代码编译正确,但是结果不正确(返回值被缩短了)。你必须在1000个调用处逐个修改。我确定你不愿做这样的工作。
OO系统的一个基础原则是数据提取。你可能会完全隐藏对像从静态程序实现消息句柄的方法,这也就是为什么所有的实例变量(一个类的非静态fields)应当是private的一个原因。
如果你让一个实例变量是public 的,那么你以后不能再改变fields ,因为这可能会破坏外面代码对它的使用。你不想仅仅因为改变了这个类,而搜索1000处对它的调用吧。
隐藏实现原则导致了对OO系统质量的一个严峻考验,你可以对一个类的定义做大的改变甚至是抛弃所有已做好的,并且替换成一个完全不同的实现吗,而且不能影响任何调用了该类的对像。这种模块化使面向对像变得更容易维护,如果没有实现隐藏,其它的OO特性也没有什么可使用点了 。
Getter Setter 方法(或者叫accessors)是危险的,同样原因,public fields 也是危险的:它们提供外部访问实现细节的方法。如果你需要改变被访问field的类型呢?你同样不得不改变accessors的返回值。你这个返回值已经被用在很多地方了,所以你必须改变所有相关代码。我想限制改变一个单个类的定义所带来的影响,不想它们影响到整个程序。
由于accessors 违反了封闭原则,你可以说如果一个系统大量的,不适宜地使了accessors就不是面向对像的。如果你参加了设计过程,并且反对那样编码,你可以发现在你的程序里几乎没有accessors. 这个过程很重要,我想在本篇快要结束的时候,对这个问题多说二句。
Getter/setter 方法的缺点并不意味着一些数据不能在系统中传递。但是,应尽可能地减少数据移动。我的经验是系统的可维护性和数据在object 之间的流动数量成反比。你可以消除所有的数据移动。
通过仔细的设计和集中精力于你所必须做的事,你可以消除你程序中大部份的getter/setter 方法。不要要求你工作需要的信息,去找含有你需要信息的object,所有的accessors 都可以找到存在理由,因为设计者不去思考关于模型:他们发送消息和runtime object ,从一个对像到另一个对像。他们错误地设计一个类,并且试着把这些类生硬地塞进动态模型。这个方法是不对的。为建立一个静态模型,你需要找到类之间的关系,并且这些关系与message flow 之间应正确相符。当一个类的对像发送消息到另一个类的对像时,这二个类之间存在一种关系。静态模型的主要目的是为动态模型捕捉这种关系信息。
如果对动态模型没有一个清晰的定义,仅是猜测怎样使用一个类的对像的话,accessor方法会经常出现在你的模型中,由于不能预知是否需要它,所以必须提供尽可能多的访问。这种猜测设计的策略是多数是很低效的,你浪费时间于写无用的方法(或给类增加不必要的功能)
当过程程序员也采纳java时,他们倾向于以熟悉的方法编码,过程化语言没有类,但是他们有C的结构体(你可以想:没有方法的类)。它看起来很正常,可以通过建造一个没有方法,除了public fields 什么都没有的类来模拟一个结构体,这些过程化的程序员读到此处时可能会认为filed应该是private,于是,他们把fields 私有化并且让它支持public accessor方法。然后进行了复杂的public access,这样子确实不能让系统OO起来。
英文:http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值