代码之家  ›  专栏  ›  技术社区  ›  kane77

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

oop
  •  10
  • kane77  · 技术社区  · 14 年前

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

    但怎么做才对呢?例如,显示/呈现“数据”类的对象——比如说人,它有名字和出生日期。类是否应具有用于显示对象的方法,其中某些呈现器将作为参数传递?这不会违反类应该只有一个目的(在本例中是存储状态)的原则,所以它不应该关心这个对象的表示。

    你能推荐一些很好的资源来展示OOP设计中的最佳实践吗?我计划在业余时间开始一个项目,我希望它是我正确OOP设计的学习项目。

    9 回复  |  直到 14 年前
        1
  •  11
  •   duffymo    14 年前

    Allen Holub用 "Why getter and setter methods are evil" 回到2003。

    很高兴你找到并阅读了这篇文章。我钦佩任何一个对自己所做的事情有批判性学习和思考的人。

    但是拿一粒盐给霍尔布先生吃。

    这一观点因其极端的立场和“邪恶”一词的使用而备受关注,但它并没有使世界着火,也没有被普遍接受为教条。

    看看c:它们实际上在语言中添加了语法糖分,使get/set操作更容易编写。这要么证实了有人认为微软是一个邪恶帝国,要么反驳了霍尔布的说法。

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

    这种极端的观点是不实际的。

        2
  •  4
  •   S.Lott    14 年前

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

    巨蟒人不会这样做。然而,他们仍在进行OO编程。显然,挑剔的能手和二传手并不重要。

    它们是常见的,因为C++和Java的局限性。但它们似乎并不重要。

    巨蟒人使用 properties 有时创建一个getter和setter函数,看起来像一个简单的属性。

    关键是“封装”是 设计 策略。它与实现几乎无关。您可以拥有所有公共属性,并且仍然是一个封装良好的设计。

    还要注意,许多人担心“其他人”直接访问属性会“违反”设计。我想这是可能的,但这样的话,全班就不能正常工作了。

    在C++(和Java)中,如果你看不到源代码,就很难理解这个接口,所以你需要很多提示。私有方法、显式getter和setter等。

    在Python中,您可以看到所有的源代码,理解接口是很简单的。我们不需要提供这么多提示。正如我们所说的“使用源代码,卢克”和“我们在这里都是成年人”。我们都能看到源代码,我们不必为堆积getter和setter而大惊小怪,就API的工作原理提供更多的提示。

    例如,显示/呈现“数据”类的对象——比如说人,它有名字和出生日期。类是否应具有用于显示对象的方法,其中某些呈现器将作为参数传递?

    好主意。

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

    这就是为什么渲染对象是分开的。你的设计很漂亮。

    没有理由解释为什么一个人对象不能调用一个通用的渲染器,并且仍然有一组狭窄的职责。毕竟,Person对象对这些属性负责,并且将这些属性传递给渲染器完全在其职责范围内。

    如果这确实是一个问题(在某些应用程序中可能是这样),您可以介绍 帮手 课程。所以 PersonRenderer 类的呈现 Person 数据。这样对人的改变也需要对 人物渲染器 --没有别的了。这就是 数据存取目标 设计模式。

    有些人将使呈现成为一个包含在人中的内部类,因此 Person.PersonRenderer 实施更严重的遏制。

        3
  •  4
  •   Péter Török    14 年前

    如果您有getter和setter,那么就没有封装。而且它们是不必要的。考虑std::string类。这是一个相当复杂的内部表示,但没有getter或setter,并且只有一个表示元素(可能)通过返回其值(即size())而被公开。这是你应该瞄准的目标。

        4
  •  2
  •   ogni42    14 年前

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

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

        5
  •  1
  •   Community Mofi    7 年前

    你为什么认为行善者是邪恶的?看到一个帖子,上面的答案正好相反:

    Purpose of private members in a class

    imho它包含了许多可以正确称为“OOP最佳实践”的内容。

    更新: 好吧,阅读你所指的文章,我更清楚地了解问题所在。这和这篇文章的挑衅标题所暗示的完全不同。我还没有阅读完整的文章,但是阿法尤的基本观点是,不应该不必要地通过无意识地添加(或生成)getter和setter来发布类字段。我完全同意这一点。

    仔细设计,专注于你必须做的事情,而不是如何 你会做到的,你消除了绝大多数getter/setter方法 你的程序。 不要要求你提供工作所需的信息; 询问有信息的对象,以便为您完成工作。

    到现在为止,一直都还不错。但是,我不同意提供这样的能手

    int getSomeField();
    

    从本质上来说会影响你的班级设计。好吧, 如果你没有设计好你的类接口 . 那么,当然,你可能会意识到这个领域应该是一个 long 而不是 int ,更改它将打破客户端代码中的1000个位置。在这种情况下,设计师应该受到责备,而不是穷人。

        6
  •  1
  •   Andreas Brinck    14 年前

    在某些语言中,比如C++,有一个概念 friend . 使用这个概念,您可以使类的实现细节仅对其他类(甚至函数)的子集可见。当你不分青红皂白地使用get/set时,你会让每个人都可以访问所有东西。

    少量使用时 朋友 是增加封装的一种很好的方法。

        7
  •  1
  •   baris.aydinoz    14 年前

    假设您的设计中有许多实体类,并且假设它们具有类似于数据的基类。为具体实现添加不同的getter和setter方法将污染使用这些实体(如许多动态类型转换)来调用所需getter和setter方法的客户机代码。

    因此,getter和setter方法可能保持原样,但您应该保护客户机代码。我的建议是将访问者模式或数据收集器应用于这些情况。

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

        8
  •  1
  •   koen    14 年前

    在下面关于内测的文章中,您将发现一个模式,以避免使用作者称之为“智能处理程序”的getter(在某些情况下)。这与Holub如何避开某些吸气剂有很多共同之处。

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

        9
  •  0
  •   Mnementh    14 年前

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