如何摆脱过程式编程的习惯,进入面向对象编程?

23

我希望能够得到一些提示,以帮助我打破我认为已经养成的不良程序编程习惯。每当我尝试使用面向对象编程(OOP)完成项目时,最终总是会回到过程化编程。我想我对OOP还没有完全信服(尽管我听说它的好处!)。

因此,我想知道是否有一些常见编程任务的实际示例,例如用户身份验证/管理、数据解析、CMS/Blogging/eComs等,这些都是我经常做的事情,但我还没有弄清楚如何在OOP中完成它们,远离过程化编程,尤其是我构建的系统往往可以工作并且效果良好。

我的开发中可能存在一些问题,一个缺点是我经常重用代码,但常常需要进行再次重写和改进,但我有时认为这是软件开发的自然演变。

但我想要改变!向我的程序员同行求助 :) 你们有什么技巧可以帮我摆脱这个讨厌的习惯吗?


2
"Tell, don't ask," 和 "Demeter法则," 和 "开闭原则." - Dave Jarvis
16
不要认为过程式编程是一种不好的习惯。如果你真的更善于使用过程式编程并能够重复利用你的代码,那么为什么要改变呢?创作者说网站应该以最简单的方式完成(这是我从阅读他的博客中得到的)。尝试面向对象编程,学习多种方法来完成你的工作,但不要认为你做错了 :) - AntonioCS
通常来说,我的同行们认为这是一种耻辱,所以在很多人提出这样的评论之后,你会想知道是否应该用另一种方式做事... - Shadi Almosri
5
你尝试过电抽搐疗法吗? :-) - Stephen C
@GogaRieger:许多人争论说C++并不是真正的面向对象,因为类对象在运行时并不存在。它更像是过程化编程,只是外表上有些对象的特征。真正的面向对象通常保留给Smalltalk、Python、Java、Ruby、Simula、CLU、LOOPS等语言。 - S.Lott
显示剩余3条评论
20个回答

20

当你无法找到充分的理由或动力时,使用面向对象编程有何意义?

你必须被需要将想法构思和操纵为对象的需求所驱动。有些人感到需要对概念、流程或功能进行感知,而不是对象,因此他们会被驱动着朝向面向概念、想法或功能流程的编程方向。

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、名称、位置等。

仅在条带打印机中找到的信息:已使用周期、花边(织物|玻璃纸)、彩色带等。

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

仅在激光中找到的信息:...

对我和许多面向对象的同学来说,我们更喜欢将所有公共信息量化为一个通用的信息封装,而不是为每种类型的打印机定义单独的结构/封装。

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

所以你不喜欢使用对象的偏好/动机告诉你,如果你不使用对象,编程生活会更容易?这意味着你更愿意自己管理所有这些结构上的复杂性。你可能没有写足够的软件来有这种感觉。

懒惰的必要性

有人说 - 必要是创造力之母(还有,贪婪是邪恶的根源)。

但对我和我的同伴来说,面对必要的懒惰才是创造力的父母(以及缺乏金钱是邪恶的另一位父母)。

因此,我敦促你采取懒惰的态度来编程,这样最短路径原则就会融入你的生活中,你会发现除了朝向对象编程之外别无选择。


+1,非常好的描述编程思维演变的过程。 - Rob Grant

15

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

步骤2. 选取你已经了解的某个东西,从面向对象的角度重新构思。这是代码道场(Code Dojo)方法。拿一个你已经了解的问题,并定义对象类。

我曾经做过这个事情,并记录了下来。

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

你可以做同样一系列的练习,以掌握面向对象的设计和编码。


那个 oodesign.com 的链接真的很好 - 我之前好像没见过。 - Erich Douglass
1
步骤2:选择一种仅面向对象的语言(如C#或其他语言) 步骤3:旧的步骤2 - RCIX
@RCIX:转而学习另一种语言并不可行,因为我希望学习的同时也要适应我的工作流程和时间,学习另一种语言会耗费太多时间(我认为/假设)。 - Shadi Almosri
@Shadi Almosri:由于您没有说明您目前掌握哪些编程语言,所以声称C#是“学习另一种语言”将是不相关的。如果您不了解C#,请更新问题以说明您已经掌握的语言。 - S.Lott
S.Lott:我在问题中加了标签,但编辑们一直在玩弄它。PHP/JavaScript 真的是我的强项...如果你愿意称它们为编程的话 :) - Shadi Almosri
@S.Lott 你好,来自2015年的问候 :) 看起来http://homepage.mac.com/s_lott/books/oodesign.html#book-oodesign已经失效了。它是被转移了还是永远丢失了? - CptSupermrkt

5
OO思维方式是基于比设计模式更基础的原则。设计模式在现今有点时髦(并且已经流行了一段时间),它们是有用的,但它们只是你可以放在更基础的东西上面的另一层,如果你想正确地进行OO编程,那么你必须学习和掌握这些基础知识。换句话说:你可以完全不使用设计模式来进行OO编程。实际上,在“设计模式”这个词汇被创造出来之前,许多人就已经很好地进行了OO编程。
现在,有些东西是你必须要学会的。我建议你从基础开始。阅读并理解Bertrand Meyer的《面向对象软件构造》第二版。这可能是关于OO编程最好的书籍,无论是广度还是深度。当然,前提是你对编程感兴趣。

我会拿一本那本书的副本 :) - Shadi Almosri

4
首先,恭喜你迈出了学习新技术的一步!我非常讨厌开发者决定不跟随技术进步。
关于从过程式编程转向面向对象编程,我认为可以做的一件事是先拿一个现有的应用程序(就像其他人所说的),然后在打开文本编辑器之前,坐下来考虑如何将应用程序的每个方面转换。我发现超过一半的OO编程是首先在你的头脑中定义概念对象。
再次,我同意大家对设计模式的建议。具体来说,我会研究MVC(Model-View-Controller)模式,因为这可能是最容易理解的模式之一。你已经写过代码,所以应该能够查看你现有的应用程序并开始将每个部分放入M,V或C类别中。
祝你好运并享受学习的过程!

谢谢你的祝福!我认为MVC是未来的方向,我已经使用CodeIgnitor作为一个很好的基础开始工作,但不幸的是我总是容易失误。我认为你说得对,可以先绘制所有不同类和所需方法+交互的映射图,甚至在编码之前,以获得对所需内容的“视觉”概述... - Shadi Almosri

4

已经有很多关于如何以面向对象的方式进行编程的信息,确实有许多优秀的书籍可以定义基本概念,但我认为问题更多地在于如何对新手来说在开发过程中坚持使用这种方法。

在面向对象编程的众多概念中,作为新手需要掌握的主要概念是封装。我的类是否知道如何照顾自己?我的类是否具有行为?如果没有,那么你没有一个类,而是一个结构体,并且你可能会写很多过程来改变它的状态(正如所说,“你回到了用Java写C的时代”)。我的类是否仅公开必需的方法以供使用?这些问题可能不会被详细阐述,但在设计类时可以考虑以下思想实验:假设每个应用程序的类都由互联网上的不同开发人员开发和维护,并且这些类还必须在互联网上相互交互。每个开发人员是否同意他们正在编写和维护的类遵守单一职责原则,因此他们不必维护应该是其他人的代码呢?
关于类接口的设计,先考虑编写使用类的所有代码。暂时不必担心底层实现。在编写第一个比特操作实现细节之前,应该能够以“类关系”为单位草拟整个程序。如果你不能做到这一点而不进行比特操作或将变量公开,那么就是回到类关系图并查看是否缺少抽象的时候了。换句话说,在编写代码之前先使用代码。如果你以前从未尝试过,请先这样做,你会惊讶地发现你的代码和接口有多干净。
虽然设计模式确实很好学习,有些非常强大,但它们通常并非本质上面向对象的,正如有些人所认为(我也同意),设计模式通常只是语言中暴露的弱点。一种语言的设计模式可能是另一种语言的基本原则。因此,在开始时,不要纠结于某种关系是否适合桥接或外观模式;这与面向对象思想无关,而与特定语言的结构相关。

+1 是针对“如何在开发过程中坚持下去”的问题的 :) 我会将你的一些观点添加到我的列表中。 - Shadi Almosri

3

不要这样做。

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

现在,你知道什么是对象,你将会发现对象并不在代码中出现。它们在运行时被发现,在机器和用户思维之间的空间中。这才是面向对象的真正含义。不幸的是,最近的学术界已经把它扭曲成了一个工程概念。这完全是错的。尽管他们试图模仿,最终的结果却是垃圾。为什么?因为当今行业所知道的“OOP”范例是建立在一个根本上错误的想法上的:对身份的分解式分析。这个想法有什么问题呢?因为身份本身是没有意义的。它是虚无的。从数学上、哲学上讲都是如此。这不是人类感知和与世界交互的方式。

权威人物:Alan Kay、Trygve Reenskaug、James (Jim) Coplien

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


2
面向对象编程中的难点在于哪些东西应该被放在一个对象中。正如您已经提到源代码的演变,这里有一个简单的指南,告诉您如何将源代码演变为面向对象的设计:
"Put stuff together that changes together."

当两个代码片段的变化速度相似时,这表明它们应该放在同一个对象中。当变化速度不同时,考虑将它们放在不同的对象中。
这也被称为“变化速度”。
如果你遵循这个指南,你的代码将自然地演变成一个良好的面向对象的设计。为什么呢?
代码片段通常具有类似的变化速度,如果它们访问一个共同的表示形式。每当表示发生变化时,使用它的所有代码片段必须同时更改。这是我们使用对象作为模块来封装表示的部分原因。在这个指南下,将接口与实现分离也是有意义的——实现更经常变化,因此具有更高的变化速度。
如果一个类有一个稳定的部分和一个不稳定的部分,那么这就是变化速度的差异,建议将稳定的部分移动到(可能是抽象的)基类中。
同样地,如果一个类有两个部分,它们同样经常变化,但在不同的时间或不同的方向上(也就是说,出于不同的原因),那么这又建议对该类进行重构。
有时用“方法”替换“类”。例如,如果方法的一行比其他行更容易更改——也许它是创建新对象实例并包含其类名的行——考虑将其移动到自己的程序中。然后子类可以通过覆盖它轻松地影响它们的更改。
我很多年前在C2维基上看到了这个概念(链接),但自那以后很少见到它被使用。我发现它非常有用。它表达了面向对象设计的一些关键潜在动机。当然,这是显而易见的。
引用: 这些是程序的变化。 还有另一种变化速度——你不希望实例变量以不同的速度改变,或者说这是潜在问题的标志。例如,在图形编辑器中,你不应该将图形和手柄放在同一个集合中,因为图形每分钟或每小时变化一次,而手柄每秒或每分钟变化一次。
在更大的视角下,你希望系统能够足够快地改变以跟上业务的变化。
PS:你应该遵循的另一个原则是“迪米特法则”,即一个对象只应该与它的朋友交谈。朋友包括:你自己、实例变量、参数、局部变量和友好集合的成员,但不包括全局变量和静态变量。

这里有一些非常好的观点,我会记下来并带走的 :) - Shadi Almosri

2

我认为首先浏览一些现有的、不错的、经过验证的面向对象代码(例如Qt源代码)会有所帮助,这样你可以感受到“如何做到”这一点。在此之后,从书中学习或创建自己的框架将更加有效。

总的来说,在阅读和练习之前先看看上下文真的很有帮助,因为它给了你机会对自己说:“哦,原来他们是这样做的!”至少对我来说是这样的。


2
您可以考虑使用CRC(类/职责/协作)卡片方法进行OO设计。这并不太可怕,只是一种通过在一堆文件卡上写东西来梳理对象应该是什么以及哪个对象应该负责哪些任务的方式,有助于澄清您的思路。
它最初被设计为OO思想的教学工具,并可能适合您。原始论文可在以下网址找到:http://c2.com/doc/oopsla89/paper.html 一个帖子建议使用Smalltalk编程来迫使你形成OO习惯,在某种程度上这是一个好建议 - Smalltalk确实对我很有好处,但是:
a)您可能没有时间学习一门新语言。如果您有条件,那太好了。
b)我曾经在大学课程中辅导过使用Smalltalk进行OO编程的课程,学生们做得非常好,证明了那句关于“你可以用任何语言编写FORTRAN”的老笑话。
最后:当我从书本上学习OO时,我得到的印象是您需要经常创建复杂的类层次结构进行子类化。当我开始与OO程序员一起工作时,我意识到这种情况并不像我想象的那么频繁。我认为每个人在学习时都会犯这个错误。

1
对于类层次结构的评论点赞。我同意这是面向对象设计中最令人望而却步的事情之一。 - Tony van der Peet
关于类层次结构的好评论。我在学校也有同样的印象,但实际上并不是这样的。在Joshua Bloch的《Effective Java》一书中,他的“优先选择组合而非继承”的条目在这方面很有说服力。 - charstar

1
唯一写出更好代码的方法就是多写代码。将你已经实现过的过程式项目转换成面向对象编程(假设你正在使用支持两种方式的语言)。第一次可能会得到一个脆弱、紧密耦合的解决方案,但没关系。拿着不好的面向对象编程实现开始重构它,直到最终找到哪些方法可行,哪些不可行。
当你准备迈出下一步时,可以阅读设计模式书籍,学习一些面向对象编程设计术语。这并非必须,但它会让你更好地掌握一些常见问题和解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接