获取器和设置器是不好的面向对象设计吗?

35

Getter和Setter很糟糕

简单浏览上述文章后,我发现Getter和Setter是不良的面向对象设计,应该避免使用,因为它们违反了封装和数据隐藏。既然如此,在创建对象时如何避免这种情况,如何对对象进行建模以考虑这一点。

在需要getter或setter的情况下,还有哪些其他替代方案可以使用?

谢谢。


请查看https://dev59.com/3HRB5IYBdhLWcg3wpYio以及被接受的答案。 - Don Roby
3
有趣的文章,但因为文章已经解答了你的问题,所以评分为-1。 - Martin Smith
相关阅读:http://c2.com/cgi/wiki?AccessorsAreEvil,http://c2.com/cgi/wiki?ForgetAboutWritingAccessors,http://c2.com/cgi/wiki?TellDontAsk - Seth
1
我真的很喜欢Allen的很多东西。他有时可能有点过于苛求,但是他擅长指出这样的问题。在他的网站上找找更多的东西:http://www.holub.com/ - Bill K
虽然已经过去十年,但我觉得这应该被提到:“Tell-Dont-Ask”有点过于简化了。有时,您正在构建一个世界,在该世界中,对象需要通过底层数据进行协作,即使这意味着通过该数据将它们相互耦合,而在这种情况下,“GetterEradicating”通常会违反比封装更多的OOP原则。相反,Martin Fowler建议在合理的情况下将数据和行为放在一起。他在这里提供了更多的信息:TellDontAsk.html - Alexander Guyer
13个回答

63

你没有理解重点。那篇文章中有效而重要的部分是:

不要请求获取需要完成工作的信息;相反,向拥有信息的对象请求为您完成该工作。

Java 风格的getter和setter泛滥是忽略这一建议的症状。


5
它们是一种“可能”的症状。问题也可能根本无法用那种方式解决。或者它们可能是使用依赖注入的“症状”。 - Stephen C

42

获取器或设置器本身并不是不好的面向对象设计。

不好的编码实践包括自动为每个成员提供getter和setter,无论是否需要(以及使本应该不公开的成员公开),因为这基本上将类的实现暴露给外部世界,违反了信息隐藏/抽象原则。有时这是由IDE自动完成的,这意味着这种做法比希望的要普遍得多。


12

6
我认为只有当属性的设置器真正是类规范(即与调用方的契约)的一部分时,才应该将其包含在API中。
任何与内部表示相关的其他数据成员都应该被隐藏。我认为至少有两个主要原因:
1)如果暴露了内部表示,未来的设计更改会更加棘手,并且需要同时修改API。
2)没有谨慎地使用设置器/获取器就公开数据成员,允许调用方非常容易地破坏类不变式。状态不一致的对象可能导致非常难以分析的错误。
不幸的是,有些框架鼓励(有时需要)为所有东西添加设置器/获取器。

5

Getter和Setter本身并不是不好的。不好的是将任何字段都设为私有,无论如何都提供getter/setter的做法。


4
我理解作者的观点是,盲目地在字段周围添加getter和setter方法并不比直接将字段公开更好。我认为作者主张谨慎地少量使用getter和setter方法,因为面向对象的思想是对象应该仅暴露必要的内容。

此外,看看Cory Petosky的回答,这只是处理信息的一种糟糕方式。如果您要求一个对象对其内部变量执行操作,那么setter或getter在哪里发挥作用? - Bill K

2
我会进一步说,只有值类型应该拥有getter。每个值类型都应该是不可变的,并配有一个可变的builder和setter。
如果您的值类型具有setter,则应在复制其他值后返回新实例。
Url.setAnchor(...)将返回一个新的Url,复制主机端口等,但覆盖锚点。
您的服务类型类不需要setter(在构造函数中设置),绝对不需要getter。您的Mailer应该在其ctor中获取主机/端口/等静态内容。如果我想发送电子邮件,那么我就调用它的send(),我的代码没有理由需要知道或想要或需要主机和其他配置值。话虽如此,创建一个MailServer类是有意义的,如下所示 // 值类型 MsilServer{ String host int port String username Strung password // all come ruth getters } // 工厂 Mailer create( MailServer)

2
获取器和设置器只是方法。他们的作用是什么?它们将字段转换为属性,这是一个重要的区别。
字段是对象中的一小部分数据或状态。属性是对象的外部可观测特征,是对象的合同的一部分。无论是直接还是通过获取器/设置器,在对象内部随处散布其信息都是一种不良设计。但是把这个说法升级到认为获取器和设置器总是不好?那只是一些缺乏好品味的程序员声称模糊的经验法则(他们一开始也并没有真正理解)是一个铁的法则。
个人而言,我倾向于尝试保持属性作为客户端脚下不会意外更改的东西。(类的客户端)我只会动态更改无法写入的属性,即使是这样,我也会尽量避免。我认为属性在许多方面都是控制实例行为的值,由客户端设置,而不是受类控制的任意事物。(这就是普通字段的用途...)

1
我建议您仔细阅读整篇文章,因为它提出了深思熟虑的论点和替代方案。问题本身太过开放,这里得到的答案只是更多的意见。

0

从这篇文章中可以看出:

何时使用访问器?首先,正如我之前所讨论的那样,如果一个方法返回一个实现了接口的对象,那么使用访问器是可以的,因为接口将你与实现类的变化隔离开来。这种返回接口引用的方法并不是真正意义上的“getter”,它只是提供对字段的访问。如果你改变了提供者的内部实现,只需修改返回对象的定义以适应这些变化。通过接口,你仍然保护了使用该对象的外部代码。

换句话说,当需要时,无论是getter还是setter方法,都应该使用接口来维护封装性。通常情况下,为返回类型或形式参数指定一个接口是一种良好的实践。


那是完全错误的,你的“做事情”类应该通过getter方法暴露它们的配置。 - mP.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接