23

我希望能得到一些技巧来帮助我摆脱这些年来我认为的程序编程的坏习惯。每次我尝试在 OOP 中做一个项目时,我最终都会恢复到程序化。我想我对 OOP 并不完全相信(尽管我认为我听说过它的一切好消息!)。

所以我想我经常执行的常见编程任务的任何好的实际示例,例如用户身份验证/管理、数据解析、CMS/博客/电子商务,都是我经常做的事情,但我还没有得到我的围绕如何在 OOP 中和远离程序性的情况下进行操作,尤其是当我构建的系统往往运行良好且运行良好时。

我认为我的开发失败的一件事是,我确实经常重用我的代码,并且它经常需要更多的重写和改进,但我有时认为这是我的软件开发的自然演变。

然而我想改变!给我的程序员伙伴们,帮助:) 关于如何摆脱这个讨厌的习惯的任何提示?

4

20 回答 20

19

当您找不到这样做的充分理由或动机时,使用面向对象编程的意义何在?

您必须受到将想法作为对象来构思和操作的需要的激励。有些人觉得需要感知概念、流程或功能而不是对象,然后他们被激励去面向概念、想法或功能流程的编程。

大约 13 年前,我从 c 切换到 c++,只是因为我需要一些想法,但 c 并不容易执行。简而言之,我的需求激发了我面向对象的编程。

面向对象的思维定势

首先,你有字节、字符、整数和浮点数。

然后你的程序开始被各种各样的变量,本地和静态的。然后您决定将它们分组到结构中,因为您认为所有通常都在传递的变量。

数据聚合

因此,就像打印机的信息一样,它的所有变量都应该包含在 Printer 结构中:

{id, name, location,
 impactType(laser|inkjet|ribbon),
  manufacturer, networkAddr},
  etc.

因此,现在,当您通过打印机信息调用一个又一个函数时,您没有具有长参数列表的函数或具有巨大串扰可能性的大量静态变量集合。

信息整合

但数据聚合还不够好。我仍然必须依赖一堆函数来处理数据。因此,我有一个聪明的想法或将函数指针合并到 Printer 结构中。

{id, name, location,
 impactType(laser|inkjet|ribbon),
 manufacturer, networkAddr,
 *print(struct printer),
 *clean(struct printer)
}

当数据包含有关如何处理/感知数据的过程时,数据就升级为信息。

信息量化

现在,激光、色带和喷墨打印机的信息集并不相同,但它们都有一组最常见的信息分母 (LCD):

任何打印机共有的信息:ID、名称、位置等

仅在色带打印机中发现的信息:usedCycles、色带(织物|玻璃纸)、colorBands 等

仅在喷墨中找到的信息:墨盒等

仅在激光中发现的信息:...

对于我和许多面向对象的群组,我们更愿意将所有公共信息量化为一个公共信息封装,而不是为每种打印机类型定义单独的结构/封装。

然后,我们更喜欢使用一个框架来管理每种类型打印机的所有功能引用,因为并非所有打印机都以相同的方式打印或清洁。

因此,您远离对象的偏好/动机告诉您,如果您不使用对象,您的编程生活会更轻松?您更愿意自己管理所有这些结构复杂性。您一定没有编写足够多的软件来产生这种感觉。

懒惰的必要性

有人说——需要是创造力之母。(还有,贪财是万恶之源)。

但对我和我的同伙来说——面对需要时的懒惰是创造力的父母。(以及缺钱是万恶之源)。

因此,我敦促您对编程采取一种懒惰的态度,以便最短路径原则在您的生活中发挥作用,您会发现但别无选择,只能将自己定位于使用对象进行编程。

于 2009-12-09T00:25:37.323 回答
15

第 1 步:阅读一本好的设计模式书籍。 http://www.oodesign.com/

步骤 2. 选择你已经知道的东西并从 OO 的角度对其进行修改。这就是 Code Dojo 方法。拿一个你已经理解的问题,定义对象类。

我这样做了——并写下了我所做的。

http://homepage.mac.com/s_lott/books/oodesign.html#book-oodesign

你可以做同一系列的练习来掌握 OO 设计和代码的窍门。

于 2009-12-08T22:10:23.833 回答
5

OO 思维方式基于比设计模式更基本的原则。如今,设计模式在某种程度上很流行(并且已经有一段时间了),它们很有用,但它们只是你可以放置更多基本东西的一层,如果你想正确地做 OO ,你绝对必须学习和掌握这些东西. 换句话说:你可以在没有设计模式的情况下完美地做 OO。事实上,我们中的许多人甚至在“设计模式”这个词被创造出来之前就已经做好了面向对象的工作。

现在,有些东西你不能没有。我建议你从基础开始。阅读并理解Bertrand Meyer 的“面向对象的软件构建”第 2 版。它可能是关于 OO 编程的最好的书,无论是在宽度还是深度上。那就是如果你对编程感兴趣。

于 2009-12-08T22:40:44.820 回答
4

关于在哪里可以找到面向对象的编程信息,已经有很多答案了。确实,有很多很棒的书可以定义基本概念,但是我认为问题更多是关于如何通过开发为新接触该方法的人“坚持下去”。

在面向对象编程的众多概念中,作为新手可以让您保持正轨的主要概念是封装。我的班级知道如何照顾自己吗?我的班级有行为吗?如果没有,那么你就没有类,你有一个结构,你可能会写很多程序改变它的状态(正如人们所说,“你又回到了用 Java 写 C”)。我的班级是否只公开公开其使用所需的方法?这些问题可能没有详细阐述,但在设计你的类时可能会考虑这个思想实验:如果你的应用程序的每个类都由互联网上的不同开发人员开发和维护,并且这些类也必须相互交互互联网。每个开发人员是否会同意他们正在编写和维护的类遵守单一责任原则,并因此为他们没有维护应该是其他人的代码而感到高兴?

关于类接口的设计,请考虑首先编写所有使用您的类的代码。不要担心金属会发生什么。你应该能够在编写你的第一个有点玩弄的实现细节之前,根据类关系来存根整个程序。如果你不能在不玩弄比特或公开变量的情况下做到这一点,那么是时候回到你的类关系图,看看你是否缺少抽象。换句话说,在编写代码之前使用您的代码。首先这样做,如果你以前从未这样做过,你可能会惊讶于你的代码和接口变得多么干净。

虽然设计模式当然很好学,而且有些非常强大,但它们通常不是本质上面向对象的,并且正如一些人认为(我倾向于同意)设计模式通常只是暴露了语言中的弱点。一种语言的设计模式是另一种语言的基本创建原则。因此,在开始时,不要纠结于某种关系是否适合作为桥梁或门面。这并不特定于面向对象的思想,这与特定语言的结构所提供的有关。

于 2009-12-09T02:04:39.193 回答
4

首先,恭喜您采取措施学习新知识!当开发人员决定不与技术一起发展时,我讨厌它。

至于从过程式编程到 OOP,我想说你可以做的一件事是使用现有的应用程序(正如其他人提到的那样),甚至在你打开文本编辑器之前,坐下来想想每个方面如何该应用程序将被转换。我发现超过一半的 OO 编程首先定义了你脑海中的概念对象。

再次,我同意大家对设计模式的建议。具体来说,我会研究 MVC(模型-视图-控制器)模式,因为这可能是最容易掌握的一种。您已经编写了代码,因此您应该能够查看现有应用程序并开始将每个部分放入 M、V 或 C 类别中。

祝你好运,玩得开心!

于 2009-12-08T22:20:09.280 回答
3

不。

首先,学习写作。其次,学习用户体验和交互设计。第三,学习商业分析。第四,学习角色塑造。

现在您知道什么是对象,您会发现在代码中找不到对象。它们在运行时被发现;在机器和用户的头脑之间的空间。这就是面向对象的真正含义。不幸的是,最近的学术界已将其扭曲为一个工程概念。没有什么比这更离谱了。并尽可能地效仿,最终结果是废话。为什么?因为今天业界所知的“OOP”范式是建立在一个根本有缺陷的想法之上的:身份的分解分析。这怎么破?因为身份本身是没有意义的。它是无效的。在数学意义上,在哲学意义上。这不是人类感知世界和与世界互动的方式。

佳能:Alan Kay、Trygve Reenskaug、James (Jim) Coplien

我多么希望我处于你的位置。:)

于 2011-01-14T21:07:36.953 回答
2

我认为首先浏览一些现有的、体面的、经过验证的面向对象的代码(例如 Qt 源代码)会有所帮助,这样您就可以了解“它是如何完成的”。在那之后,从一本书中学习或创建自己的框架会更有效。

一般来说,在阅读和练习之前先了解上下文确实很有帮助,因为它让你有时间对自己说:“哦,这就是他们这样做的原因!” 至少这对我来说是这样的。

于 2009-12-08T22:17:56.303 回答
2

您可能会考虑使用 CRC(类/责任/协作)卡方法进行 OO 设计。这没什么太可怕的——只是一种通过在一堆文件卡上写下东西来帮助澄清你的想法来整理你的对象应该是什么,以及哪个对象应该负责哪些任务的方法。

它最初是作为面向对象思想的教学工具而设计的,可能对你有用。原论文在:http ://c2.com/doc/oopsla89/paper.html

上面的一张海报建议在 Smalltalk 中进行编程,以迫使您养成 OO 习惯,并且在某种程度上这是一个很好的建议 - Smalltalk 确实对我有很多好处,但是

a) 你可能没有空闲时间学习一门新语言。如果你这样做,那就太好了。

b) 我曾经使用 Smalltalk 辅导一门大学 OO 编程课程,学生们出色地证明了“你可以用任何语言编写 FORTRAN”这个古老的笑话。

最后:当我(从书本上)学习 OO 时,我的印象是您进行了很多子类化,从而创建了复杂的类层次结构。当我开始与 OO 程序员一起工作时,我意识到这并没有我想象的那么频繁。我想每个人在学习的时候都会犯这个错误。

于 2009-12-09T00:59:24.213 回答
2

OO 的难点在于哪些东西应该放在一个对象中。正如您已经提到源代码的演变,这里有一个关于如何将源代码演变为面向 OO 设计的简单指南:

"Put stuff together that changes together."

当两段代码具有相似的变化速度时,这暗示它们应该放在同一个对象中。当变化速度不同时,考虑将它们放在不同的对象中。

这也称为“变化速度”。

如果您遵循该准则,您的代码自然会朝着良好的 OO 设计发展。为什么?

如果代码片段访问一个共同的表示,它们通常具有相似的变化速度。每次表示更改时,使用它的所有代码都必须立即更改。这是我们使用对象作为模块来封装表示的部分原因。在本指南下,将接口与实现分开也是有意义的——实现更频繁地更改,因此具有更高的更改速度。

如果一个类有一个稳定部分和一个不稳定部分,那就是变化速度的差异,这表明将稳定部分移动到一个(可能是抽象的)基类。

类似地,如果一个类有两个部分变化相同,但在不同的时间或不同的方向(也就是说,出于不同的原因),那么这再次表明重构该类。

有时将“类”替换为“方法”。例如,如果方法的一行可能比其他行更频繁地更改——也许它是创建新对象实例并包含其类的名称的行——考虑将其移动到自己的例程中。然后子类可以很容易地通过覆盖它来影响它们的变化。

很多年前我在 C2 wiki 上遇到过这个概念,但从那以后我就很少看到它使用了。我觉得它非常有用。它表达了面向对象设计的一些关键的潜在动机。当然,它因此非常明显。

这些是程序的变化。还有另一种变化速度的感觉——你不希望实例变量以不同的速率变化,或者说这是潜在问题的征兆。例如,在图形编辑器中,您不应将图形和句柄保留在同一个集合中,因为图形每分钟或每小时更改一次,而句柄每秒或每分钟更改一次。

从更大的角度来看,您希望系统能够以足够快的速度更改以跟上业务的变化。

PS:你应该遵循的另一个原则是“得墨忒耳法则”,即一个对象应该只与它的朋友交谈。朋友是:您自己、实例变量、参数、局部变量和友好集合的成员——但不是全局变量和静态变量。

于 2009-12-09T03:07:42.223 回答
1

我发现真正有助于巩固 OOP 对我的好处的一件事是使用模拟对象框架(例如EasyMock)编写单元测试。一旦开始以这种方式进行开发,您就可以看到类如何帮助您将模块隔离在接口后面,并且还可以简化测试。

要记住的一件事是,当人们第一次学习 OOP 时,通常会过分强调继承。继承有它的位置,但它是一个很容易被过度使用的工具。组合或简单的接口实现通常是更好的做事方式。不要试图通过继承来重用代码,因为从多态性的角度来看,继承树毫无意义。替代原则是使继承/接口实现强大的原因,而不是您可以通过子类化重用代码的事实。

于 2009-12-09T07:19:02.020 回答
1

编写更好的代码的唯一方法是编写更多的代码。拿一个您已按程序实现的项目并将其转换为 OOP(假设您使用的语言同时支持这两种语言)。第一次你可能会得到一个脆弱的、紧密耦合的解决方案,但这没关系。采用糟糕的 OOP 实现并开始将其重构为更好的东西。最终,您会弄清楚哪些有效,哪些无效。

当您准备好进行下一步时,请拿起一本设计模式书籍并学习一些 OOP 设计术语。这不是绝对必要的,但它会让您更好地掌握一些常见问题和解决方案。

于 2009-12-08T22:23:47.400 回答
1

我认为你应该通过研究过程编程的所有缺点来说服自己,例如(一些流行语,注意):范围,状态......实际上你可以通过阅读设计模式的例子来提取许多术语(阅读:一起使用对象的常见示例。)

强迫自己去学习一些你不相信的东西不会让你到任何地方。开始对您早期的工作非常关键并对其进行重构以避免复制代码和使用全局范围,您会发现自己想要更多。

于 2009-12-08T22:36:56.450 回答
1

对我来说,OOP 的啊哈时刻是我第一次查看代码并意识到我可以将常见的东西重构为基类。您清楚地了解代码和重用的方式,但您需要考虑类而不是过程。通过用户身份验证,很明显您将拥有用户名和密码,现在它们进入基类,但是如果您还需要 tokenId 怎么办,重新使用现有的登录基类,并从中创建一个新的子类有了新的行为,您现有的所有代码都可以正常工作。

看看它是如何为你工作的。

于 2009-12-09T00:10:10.050 回答
1

好吧,首先设计模式是对你的编程进行模式化的最糟糕的事情。

这只是一大堆东西。这与 OOP 无关,其中大多数(例如单例)经常用于所有错误的原因(即初始化)。其中一些你必须使用,所以告诉你它们是没有意义的,其他的会适得其反,其余的只是特殊情况。如果您尝试以这种方式学习任何东西,那么一切都将开始看起来像是有人为一个非常特殊的问题或因为他们需要无限的通用性而提出的一些奇怪的小玩意儿(这很少是真的)。不要让人们无缘无故地让你使用一百万个迭代器和模板,并使事情复杂十倍。

真正的 OOP 是一个简单的主题,但变得非常复杂。不幸的是,在 C++ 中它有很多问题,但真正简单的虚方法才是最重要的。像 java 接口对象一样使用的纯虚拟基类是最有用的,但也只是普通的虚拟方法在这里和那里会派上用场。

大多被夸大了。它也不能很好地解决所有问题。如果你制作数据库和 gui 的东西,它很适合。如果您制作系统工具,它通常没有那么有用。

于 2009-12-09T01:25:41.587 回答
0

我发现学习在编程中训练抽象的一种非常密集的方法是构建一个具有定义功能的 OOP 库,然后同时在该库上实现两个具有相似但仍然不同要求的项目。

这非常耗时,您需要先了解 OOP 的基础知识(S.Lott 在另一个答案中有一些很棒的链接)。不断的重构和大量的“Doh!” 时刻是规则;但我发现这是学习模块化编程的好方法,因为我所做的一切在其中一个项目的实施中立即引起注意。

于 2009-12-08T22:08:43.293 回答
0

一个很好的步骤是从 OOP 框架开始,您仍然可以在框架中编写程序代码,但随着时间的推移,您可以改进您的编码习惯并开始将功能转换为对象。

此外,阅读有关模式和数据建模的内容将为您提供更多关于以 OOP 风格编写逻辑的想法。

于 2009-12-08T22:11:21.790 回答
0

我认为首先学习理论很重要。所以看书是一个好的开始。

于 2009-12-08T22:50:51.850 回答
0

简单练习。如果您已经阅读了有关 OOP 的所有内容,并且您对 OOP有所了解,并且您知道在您的语言 PHP 中实现的 OOP 原则......那么只需练习、练习和练习更多。

现在,不要将 OOP 视为锤子而将其他所有内容视为钉子,而应尝试在项目中至少包含一个类。然后看看你是否可以在另一个项目等中重用它。

于 2009-12-08T22:32:06.327 回答
0

学习一门新的语言,它可以帮助你温和地转向 OOP。Java 很好,但有点臃肿。但是它的系统库主要是面向对象的,所以你不得不使用对象。迁移到另一种语言还可以帮助您重复使用旧代码 :-)

于 2009-12-08T22:40:35.487 回答
0
  1. 我相信 OOP 的机制似乎完全是武断的,并且在您阅读有关设计模式的书并理解它的“原因”之前没有任何意义。我推荐Head First Design Patterns。在我拿起这本书并看到它实际上有什么好处之前,我认为 OOP 很荒谬而且完全没用。

  2. 当您了解函数指针以及它与间接函数调用和后期绑定的关系时,OO 会更有意义。在 C、C++ 或 D 中使用函数指针一段时间,了解它们的用途和工作方式。OO 的多态性/虚函数部分只是在此之上的另一抽象层。

  3. 程序是某些工作的正确工具。不要表现得好像是错的。恕我直言,所有三种主要范式(过程、OO、功能)即使在细粒度级别上,在单个模块中也是有价值的。我倾向于:

当我的问题很简单时(或者我已经用函数式和 OO 充分考虑了它,我现在有一个我认为很简单的子问题)并且我想要最直接的解决方案,而没有太多的抽象阻碍,程序化是好的。

当我的问题更复杂并且有很多在问题域上下文中有意义的状态时,面向对象是好的。在这些情况下,状态的存在不是实现细节,但确切的表示是我更喜欢抽象出来的表示。

当我的问题很复杂但没有在问题域级别有意义的状态时,功能性很好。从问题域的角度来看,状态的存在是一个实现细节。

于 2009-12-09T00:53:37.797 回答