没有吸气剂的正确OOP设计?

时间:2010-04-01 10:19:08

标签: oop

我最近读到getter / setter是邪恶的,我不得不说这是有道理的,但是当我开始学习OOP时,我学到的第一件事就是“封装你的字段”,所以我学会了创建类给它一些字段,为它们创建getter,setter并创建我初始化这些字段的构造函数。每当其他一些类需要操作这个对象(或者例如显示它)时,我将它传递给对象并使用getter / setter对其进行操作。我可以看到这种方法存在问题。

但怎么做对了?例如显示/渲染对象是“数据”类 - 让我们说人,有姓名和出生日期。该类是否有显示对象的方法,其中一些Renderer将作为参数传递?这不会违反类只有一个目的(在这种情况下是存储状态)的原则,所以它不应该关心这个对象的表示。

您能否提出一些优秀的资源,其中介绍了OOP设计的最佳实践?我打算在业余时间开始一个项目,我希望它成为我正确的OOP设计的学习项目..

9 个答案:

答案 0 :(得分:11)

Allen Holub在2003年与"Why getter and setter methods are evil"发生了重大关系。

你找到并阅读这篇文章真是太棒了。我很钦佩任何正在学习和批判性地思考他们正在做什么的人。

但请将霍洛布先生带上一粒盐。

这一观点因其极端的地位和“邪恶”这个词的使用得到了很多关注,但它没有引起世界的火热或被普遍接受为教条。

看看C#:他们实际上为语言添加了语法糖,使得get / set操作更容易编写。这或者证实了某人认为微软是一个邪恶的帝国,或者与霍罗布先生的陈述相矛盾。

事实是人们编写对象以便客户可以操纵状态。这并不意味着用这种方式写的每个对象都是错误的,邪恶的或不可行的。

极端观点不切实际。

答案 1 :(得分:4)

  

“封装你的字段”所以我学会了创建类给它一些字段,创建getter,setter

Python人不这样做。然而,他们仍在进行OO编程。显然,挑剔的吸气剂和制定者并非必不可少。

由于C ++和Java的限制,它们很常见。但它们似乎并不重要。

Python人员有时会使用properties创建一个看似简单属性的getter和setter函数。

重点是“封装”是一种设计策略。它与实现几乎没有任何关系。您可以拥有所有公共属性,并且仍然是一个很好的封装设计。

另请注意,许多人担心通过直接访问属性“违反”设计的“其他人”。我想这可能会发生,但那时课程就会停止正常工作。

在C ++(和Java)中,您无法看到源代码,因此很难理解界面,因此需要大量提示。私有方法,显式getter和setter等。

在Python中,你可以看到所有的源代码,理解界面是微不足道的。我们不需要提供这么多提示。正如我们所说的“使用来源,卢克”和“我们都是成年人”。我们都能够看到源代码,我们不需要对getter和setter上的堆积进行繁琐的处理,以提供有关API如何工作的更多提示。

  

例如显示/呈现“数据”类的对象 - 让我们说具有姓名和出生日期的人。该类是否有显示对象的方法,其中一些Renderer将作为参数传递?

好主意。

  

这不会违反类只有一个目的(在这种情况下是存储状态)的原则,所以它不应该关心这个对象的表示。

这就是渲染对象是分开的原因。你的设计很不错。

没有理由为Person对象无法调用通用渲染器并且仍然具有一组狭窄的职责。在所有Person对象负责属性之后,将这些属性传递给Renderer完全符合它的职责。

如果它确实存在问题(可能在某些应用程序中),您可以引入 Helper 类。因此PersonRenderer类会对Person数据进行渲染。这样,对Person的更改也需要更改PersonRenderer - 而不是其他任何内容。这是数据访问对象设计模式。

有些人会让Render成为一个包含在Person中的内部类,所以Person.PersonRenderer强制执行一些更严重的遏制。

答案 2 :(得分:4)

如果你有getter和setter,你就没有封装。而且他们没有必要。考虑一下std :: string类。这有一个非常复杂的内部表示,但没有getter或setter,只有一个元素的表示(可能)只是通过返回它的值(即size())来暴露。那是你应该瞄准的那种东西。

答案 3 :(得分:2)

为什么它们被认为是邪恶的基本概念是,类/对象应该导出函数而不是状态。对象的状态由其成员组成。 Getters和Setter允许外部用户在不使用任何函数的情况下读取/修改对象的状态。

因此,除了可能具有Getters的DataTransferObjects和用于设置状态的构造函数之外,只应通过调用对象的功能来修改对象的成员。

答案 4 :(得分:1)

为什么你认为吸气剂是邪恶的?查看一篇证明相反的答案的帖子:

Purpose of private members in a class

恕我直言,它包含许多可称为“OOP最佳实践”的内容。

更新:好的,阅读您所指的文章,我更清楚地了解问题所在。这与文章的挑衅性标题所暗示的完全不同。我还没有阅读完整的文章,但AFAIU的基本观点是,不应该通过盲目添加(或生成)的getter和setter来不必要地发布类字段。在这一点上我完全赞同。

  

通过精心设计并专注于你必须做的事情,而不是如何做   你会做到这一点,你消除了绝大多数的getter / setter方法   你的计划。 不要求您提供完成工作所需的信息;   询问有信息的对象为你做的工作。

到目前为止一切顺利。但是,我不同意提供像这样的吸气剂

int getSomeField();

固有地妥协你的班级设计。好吧,如果你没有很好地设计你的类接口。然后,当然,可能会发生这样的事情,即您认识到该字段应该是long而不是int,并且更改它会在客户端代码中突破1000个位置。恕我直言,在这种情况下设计师应该受到责备,而不是穷人的吸气。

答案 5 :(得分:1)

假设您的设计中有许多实体类,并假设它们具有像Data这样的基类。为具体实现添加不同的getter和setter方法将污染使用这些实体(如许多dynamic_cast)的客户端代码,以调用所需的getter和setter方法。

因此,getter和setter方法可能保留在原来的位置,但您应该保护客户端代码。我的建议是为这些案例应用访客模式或数据收集器。

换句话说,问问自己为什么我需要这些访问器方法,如何操作这些实体?然后在Visitor类中应用这些操作以保持客户端代码清洁,还可以扩展实体类的功能而不会污染其代码。

答案 6 :(得分:1)

在某些语言中,如C ++,有friend的概念。使用此概念,您可以使类的实现细节仅对其他类(甚至函数)的子集可见。当您不加选择地使用Get / Set时,您可以让每个人都能访问所有内容。

谨慎使用时friend是增加封装的绝佳方法。

答案 7 :(得分:1)

在下面关于endotesting的文章中,你将找到一种模式,以避免使用作者称为“智能处理程序”的getter(在某些情况下)。它与Holub如何避免一些吸气剂有很多共同之处。

http://www.mockobjects.com/files/endotesting.pdf

答案 8 :(得分:0)

任何公开的内容都是该类API的一部分。改变这些部分可能会破坏其他东西,依赖于此。公共字段不仅与API连接,而且具有内部表示,可能存在风险。示例:将数据作为数组保存在字段中。此数组是公共的,因此可以从其他类更改数据。稍后您决定切换到通用列表。将此字段用作数组的代码已损坏。