我尝试寻找这个问题的答案,但很难用语言表达,而且许多问题要么是关于如何创建类,要么是关于如何做非常具体的事情。我需要更多的实际概述 - 我是自学的,我了解对象是什么(以及如何创建它们),但我从未见过关于何时创建对象的良好解释。
这听起来可能是一个奇怪的问题,其答案是“总是”,但我注意到我的 OOP 代码往往在某些时候开始看起来是程序性的。如何结合我对这两种范式的理解,以便编写结构良好的面向对象代码?在某些时候,有一些主要函数或代码体需要创建变量并实例化对象,然后执行诸如执行条件测试之类的操作,因此我正在努力弄清楚应该将哪些内容作为对象放入代码中。
也许答案是我上面提到的所有这些点都应该由另一个对象封装。我不知道——这就是我努力从理解的角度前进的地方。
这可能不太适合 Stackoverflow,但这是一个非常好的问题,应该得到解决。您无意中遇到了编程中的一个常见问题:大多数“OOP”程序实际上并不是面向对象的。
OOP 的全部意义在于封装复杂性。Alan Kay 最初是从生物细胞的角度来设想的,细胞的所有机械都与外界隔离,你只能通过进出的消息与细胞进行交互。因此,一个对象封装了一个完整的系统,它本身是由对象组成的,并且可以与其他对象组合成更大的对象。所有事物都会通过消息进行通信,没有人可以窥视其他人的内心。这就是面向对象编程的含义,许多写作仍然受到这种思想的影响。但大多数自称 OOP 的东西都不会这样做(以这种方式工作的东西今天通常被称为“Actor 模型”,它可以是一种非常有效且强大的编程方式,在像 Erlang 这样的语言中得到体现)。
如今,大多数 OOP 系统都认为对象是一件不太有趣的事情(至少在我看来):只是数据和可以作用于该数据的方法的组合。业界对于这是否是一个特别有用的构造存在很大争议(相对于将数据与方法分开的功能范式,或者纯粹关注功能而不是类的鸭子类型范式)。
但对于你在实践中的问题,我想说,在大多数情况下,我们创建对象有两个原因:聚集数据和封装责任。这些确实是不同的东西,但在许多流行语言中它们都被视为“对象”。
在“聚集数据”的情况下,当您拥有逻辑上应该组合在一起的数据集合时,您将创建一个新对象。例如,一个点是两个坐标的集合,这是一个完美的“数据对象”。
关于“封装责任”,指的是我们所说的“单一责任”原则。对象应该对您可以命名的一件“事物”承担全部责任。例如,那个“东西”可能是“网络连接”或者可能是“绘制这个窗口”。您有一个 Connection 类和一个 Window 类等。真正的关键是命名。当你很容易命名它时,你就知道你有一个很好的类,并且它所做的一切似乎都遵循你给它起的名字。当很难命名时,您可能创建了错误的类。
类层次结构的关键教训称为替代原则(由 Barbara Liskov 形式化)。如果您要创建一个子类,则它必须能够在可以使用其超类的任何地方使用。所以柯基犬就是狗,因为狗能做的一切,柯基犬都能做。但令人惊讶的是,正方形不是矩形。可以创建具有两种长度的矩形。正方形不可能。方矩形问题可能是继承错误的最常见原因。一个值得吸取的教训是,对于许多问题来说,继承都是错误的工具。您应该倾向于组合多个对象而不是继承。因此,在创建新类时,浅层次结构通常是最好的。
正如您所发现的,您所看到的许多所谓的“OOP”只是包裹在对象中的过程编程。这对新人来说非常困惑。对于我们这些几十年来一直这样做的人来说,这相当令人困惑。你只需要看到这一点。