为什么数据类被认为是代码异味?


18

文章称,数据类是一个“代码味道”。原因:

当新创建的类仅包含几个公共字段(甚至可能有少数getter / setter)时,这是很正常的事情。但是对象的真正威力在于它们可以包含行为类型或对其数据的操作。

为什么对象仅包含数据是错误的?如果该类的核心职责是代表数据,那么添加对数据进行操作的方法是否会违反“ 单一职责原则”


1
这将在很大程度上取决于语言功能。例如,在Python中,“ field”及其访问器之间没有区别,除非您不愿意用Python编写Java
jscs

1
我认为仅拥有一些数据类本身并不是代码的味道,但是如果大多数类都是这样,那么我们正在谈论“贫血领域”反模式en.wikipedia.org/wiki/Anemic_domain_model
TulainsCórdova16年

1
我看不出这个问题是如何重复的。另一个问题是关于在OO中使用数据类,而这个问题是关于数据类的缺点-完全不同的主题。
米洛斯·姆多维奇

您可能需要阅读有关stackoverflow的答案,该答案与此处被投票最多的答案(假定富域模型的劣势并像经过证明的事实一样)相比,差异更大。stackoverflow.com/questions/23314330/…–
McLovin

Answers:


31

拥有纯数据对象绝对没有错。坦率地说,这本书的作者不知道他在说什么。

这种想法源于一个古老的,失败的想法,即“真正的OO”是最好的编程方式,而“真正的OO”则是关于“丰富的数据模型”的,其中混合了数据和功能。

事实表明,事实恰恰相反,尤其是在这个多线程解决方案领域。纯函数与不可变的数据对象相结合,是一种更好的编码方式。


1
只是想补充一点,纯数据对象对于建模关系,验证,控制访问/更改而言是无价的。
阿德里安

2
尽管将仅将不可变数据对象的实例作为参数的纯函数实现为数据对象上的方法是很好的。
RemcoGerlich

7
如果函数采用该数据类型的参数,则它已经与数据耦合。
RemcoGerlich

4
投票否决,因为这是不正确的,或充其量只是见解。在OO语言中,使对象包含数据(仍然是不可变的!)和对其起作用的方法通常是有意义的。在其他语言范式中,纯函数和单独的数据非常有用,但是如果您要执行OO,请完全进行OO。
Marnen Laibow-Koser,

3
现实向我们展示了很多东西。对于教条式的知识,-1表示“其他人都失败了”。而且,作者并没有说纯数据对象是“错误的”,只是说它们是“代码气味”的,值得质疑。我仅感到遗憾的是,我为我的国家投下了反对票。:-)
user949300 '19

7

拥有纯数据对象绝对没有错。作者的观点未与我认识的软件开发人员共享。

特别是对于数据库映射,您通常具有实体类,这些实体类仅包含存储在数据库中的字段以及getter和setter。维基百科休眠(框架)

许多工具/框架使用的Java bean漏洞概念都是基于称为bean的数据类,这些数据类仅包含字段以及相关的getter和setters。Wikipdia JavaBeans

Fazit:
如果有人声称某些东西“不好”或“有代码气味”,则应始终查找给出的原因。如果原因不能说服您,请向其他人询问更好的原因或其他意见。(就像您在此论坛中所做的一样)


作者没有说纯数据对象是“错误的”。他们说纯数据对象是一种“代码气味”,这意味着您应该对使用它们进行三思。
user949300 '19

1
@ user949300,您似乎很困惑。如果作者将其视为代码气味,则表示它们可能有问题。如今,由于它们被广泛认为是一种很好的做法,因此它们显然不是代码气味。因此,史密斯先生42是对的:他们绝对没有错。
David Arno

4

马丁·福勒(Martin Fowler)的一个很好的论点为何:

“不要问这个原则,可以帮助人们记住面向对象是将数据与对该数据进行操作的功能绑定在一起。它提醒我们,我们不向对象索要数据并对该数据进行操作,而是而是应该告诉对象该怎么做。这鼓励将行为转移到对象中以与数据一起使用。”

https://martinfowler.com/bliki/TellDontAsk.html


1
这里的问题是,Fowler通过将功能从询问更广泛的范围更改为仅询问对象范围,来人为地限制“告诉不询问”。他们仍然在问。实际上,可以通过“实话实说”通过参数列表告诉那些函数,从而进一步“告诉别人”。因此,我们得出了数据对象和单独的(数据明智的)函数,它们是“告诉不要问”的真正实现。因此,与其说说数据类是代码的味道,不如说它是一个很好的论点,但事实证明事实恰恰相反。
David Arno

2
@DavidArno您忘记了封装和隐藏。您告诉一个对象执行一个成员方法,该成员方法进入该对象的黑匣子,并尽一切努力来获得答案。如果从外部询问对象,则无法访问其私有状态,因此该对象公开的状态多于明智的选择,或者询问者必须跳过不必要的循环。我不明白为什么要在OO环境中“询问”一个对象。(当然,其他编程范例可能会要求使用不同的方法。)
Marnen Laibow-Koser,

2
@DavidArno当然,您可以baz参数作为参数传递给静态方法,但是要做到这一点,您首先需要向对象询问。也许在方法是主要方法的编程范例中(例如函数式编程),这是有道理的,但是在OO环境中,绝对没有意义,因为对象是主要方法,并且应同时包含数据和作用于其上的功能。据我所知,您声称从对象中删除方法会增加封装,这也恰恰是倒退,因为这意味着您现在已经baz出现在对象外部。
Marnen Laibow-Koser,

1
@ MarnenLaibow-Koser,我不声称自己正在做“面向对象”。我编写代码,并且使用良好的技术来做到这一点。那些技术是来自功能范式还是OO范式,或者谁该死的范式对我来说都不重要。选择一个范式并尽可能地坚持下去就是纯粹的教条。这是坏的。这太荒谬了。它使您窒息并导致劣质代码。不要这样
David Arno

1
@DavidArno相反,如果您完全遵循一个范式(任何体面的范式,而不仅仅是OO),您将获得强大的高级抽象和逻辑上一致的,可维护的代码。我并不是说这是教条,而是务实。我已经看到并维护了太多的代码,这些代码显然是按照您的态度生成的,在这种态度下,作者并没有真正致力于所使用系统的逻辑一致性。这很难理解,难以维护且难以修改。没有一个范式是完美的,但是通常很难理解混合(除非仔细考虑)。
Marnen Laibow-Koser,

2

您需要了解的是,有两种对象:

  • 行为的对象。这些应避免公开访问其大多数/任何数据成员。我希望只为这些定义的访问器方法很少。

    一个示例就是一个已编译的正则表达式:创建该对象是为了提供某种行为(将字符串与特定的正则表达式进行匹配,并报告(部分)匹配项),但是编译后的正则表达式如何执行其工作与用户无关商业。

    我写的大多数类都属于此类。

  • 对象实际上只是数据。这些应仅将其所有成员声明为公共(或为他们提供完整的访问器集)。

    一个例子是一个类Point2D。绝对不需要为此类的成员确保不变,并且用户应该能够仅通过myPoint.x和访问数据。myPoint.y

    就我个人而言,我并没有太多使用此类,但我想我没有编写更大的代码来在某个地方不使用此类。

精通面​​向对象包括意识到存在这种区别,并学习将类的功能归为这两类之一。


如果使用C ++编写代码,则可以通过class对第一类对象和struct第二类对象使用来明确区分。当然,两者是等效的,除了那class意味着默认情况下所有成员都是私有的,而默认情况下则将struct所有成员声明为公共。这正是您想要传达的信息。


1
关于
下降投票

感谢您的回答。当人们无缘无故投票时,我讨厌它。如果您对答案有疑问,请说明原因
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.