欢迎光临
我们一直在努力

面向过程还是面向对象

面向对象在如今的软件行业是非常著名的一个术语,在很多人看来,面向过程和面向对象都是一种软件技术。例如把面向过程归纳为结构化程序设计,离散多普勒图、耳模型、超声心动图矩阵等,而面向对象则被归纳为继承、封装、多态、复用等等具体的技术.事实上,上述的所有技术都只是人们在采用不同的方法来认识和描述这个世界时所采用的工具,它们都只是表征而不是本征。

UML创始人之一格雷迪.布奇曾说过“在面向对象兴起运动之前,编程以过程为中心,例如结构化设计方法。然而,系统已经到达了超越其处理能力的复杂性极点。有了对象,我们能够通过提升抽象级别来构建更大的、更复杂的系统–我认为,这才是面向对象编程运动的真正胜利。”

从本质上说面向过程和面向对象是一个古已已有之的认识论的问题,之所以面向对象方法会兴起,是因为这种认识论能够帮助我们构造更为为复杂的系统来解释越来越复杂的现实世界。认识到这一点,我们应该知道比掌握具体技术更重要的是掌握认识论所掌握的方法和分析过程,只有掌握了方法才能自如地使用工具。

面向过程

面向过程的方法

面向过程的分析方法,就是:
先找到事情从哪里开始 → 然后一步一步往下推 → 把中间每一步都分析清楚 → 直到事情结束。
而且中间的每一步都很重要,缺一不可。

因为面向过程的软件非常依赖“稳定、正确、结构清晰的数据”,所以人们发展出了关系型数据库、三大范式和 ER 模型,来保证数据不乱、不丢、不冲突。

系统能不能正常运行,关键不只是程序写得对不对,更关键的是:

  • 数据准不准确
  • 数据全不全

程序错一次可能还能修,数据错了,系统就“逻辑性崩塌”,人们不是随便存数据,而是:

  • 用 主键(PK):唯一标识一条数据

  • 用 外键(FK):表达“数据之间的关系”

数据有固定结构,字段有明确含义,表之间有清晰边界。利用关系理论,即数据库的三大范式来保证它们的完备性和一致性,避免冗余,防止更新异常,保证数据一致。

  • 第一范式:字段不可再拆

  • 第二范式:消除部分依赖

  • 第三范式:消除传递依赖

因为程序是按流程一步步跑的,而流程离不开稳定可靠的数据,所以人们必须先把数据设计好。为了防止数据乱套,就引入了主键、外键、三大范式和 ER 模型。正是在这种以流程为核心的软件时代,关系型数据库和 ER 建模方法才会被广泛使用。

图1.1 传统型商务

面向过程的方法,默认世界是“稳定的、可预测的”,事情能按固定步骤一步步发生;但现实世界尤其是信息化时代变化太快,原先设定好的步骤和因果关系经常被打破,所以单纯靠面向过程已经越来越吃力了,这也是为什么后来大家开始转向 SOA、按需应变。

面向过程的困难

当业务还比较固定时,用面向过程的方法,我们还能把一次销售从开始到结束完整地分析清楚;但当业务变成“随需应变”的模式后,事情就完全不一样了。此时,业务不再沿着一条固定流程展开,而是根据当下的商业需要不断重新组合。流程本身变得异常复杂,已经很难再用一个完整、稳定的过程去描述。

更麻烦的是,这些业务节点之间并不一定存在明确的因果关系,它们只是被临时拼接在一起,用来满足某个特定的业务场景。即便我们投入大量精力,把所有可能的组合方式都提前设计出来,也很快会发现:随着某个业务事件或规则的变化,这些组合方式立刻就失效了,又需要重新调整。图 1.2 中密密麻麻的问号,正是这种不确定性和困惑的真实写照。

图1.2 随需而变的商务

面向过程之所以越来越吃力,本质原因在于:它把世界看成一条条事先规划好的流程,认为系统是由许多紧密相连的小环节组成的,而且这些环节之间有着清晰、稳定的因果关系。在需求简单、变化不大的情况下,这种方法确实非常有效,就像一台照相机:光线通过镜头,落在胶片上,再经过冲洗,就能完整地还原现实世界的信息。

但现实世界远比我们想象得复杂。系统中影响结果的因素太多,而且相互之间不断作用。就像“蝴蝶效应”所描述的那样,一个微小的变化,都可能彻底打乱我们原先设计好的流程,使整个过程变得面目全非。

这并不是面向过程的方法本身有问题,而是因为系统的复杂度已经远远超出了我们可以穷举和分析的范围。我们既无法提前考虑所有可能的因素,也无法理清它们之间全部的因果关系,更不可能把这样一个过程完整、准确地模拟出来。

在精力和计算能力都有限的前提下,我们只能放弃对“整体过程”的完全掌控,转而寻找一种新的方式,把复杂系统拆解成多个我们可以理解和控制的部分。这种思路的转变,就好比造一辆汽车:如果试图一次性把整车造出来几乎不可能,那就把它拆分成发动机、底盘、车身等零部件,分别制造,最后通过事先约定好的接口把它们组合起来,形成最终的产品。

这种从“整体流程控制”到“模块拆解与组合”的转变,正是后续 SOA、组件化和服务化思想出现的根本原因。

面向对象

面向对象的方法

面向对象的方法换了一种看世界的方式。它不再把世界理解为一条条预先定义好的流程,而是把世界看成由许多彼此独立的“对象”组成。平时,这些对象各自做各自的事情,互不干扰;只有在外部事件或需求触发时,它们才会按照一定的规则进行信息交互。正是这些交互,在某个特定时刻,临时构成了我们所看到的“过程”。

在没有外部刺激的情况下,对象本身是相对静止和稳定的,它们只负责维护自己的状态和行为,而不关心整个世界的运行流程。

从面向对象的角度来看,看似紧密耦合的小系统,其实并不是一个不可分割的整体,而是由多个性质不同、职责各异的对象组合而成的。正是这些对象按照一定规则协作,才呈现出系统整体的功能和特性。

在微观层面,每个对象都具有一些非常重要的特征。首先,对象有清晰的边界,对外只暴露交互方式,内部实现细节对外是不可见的,这就是封装;其次,对象之间可以形成组合关系,多个对象组合后可以对外表现为一个新的对象,这就是聚合;对象还可以被复用和扩展,新对象可以继承已有对象的能力,这被称为继承;

同一个对象往往可以以不同的方式对外提供能力,不同的使用场景只关注它的某一个“侧面”,这就是接口;而当多个对象对外表现出相同的接口,却在内部采用不同的实现方式时,我们就称之为多态。

正是这些特性,使得系统不再依赖于一条固定、脆弱的整体流程,而是由一组稳定、可演化的对象,通过不断变化的交互方式来适应复杂多变的现实世界。

从宏观角度看,对象其实是“局部视角”的。它并不知道整个系统在做什么,也不关心自己的行为最终会对整体产生什么影响。对象只关注和自己直接相关的一小部分对象,这些被称为它的依赖;它们之间通过有限的信息交换建立联系,这种关系我们称为耦合。

同时,对象也是高度自我保护的。即使是在协作关系中,它也会严格守住自己的边界,只允许外界通过预先定义好的入口与它交互,而不会暴露任何内部细节。这些对外开放的入口,就是我们所说的方法。

听到这里,很多人可能会产生疑问:如果对象彼此之间并不了解整体,也没有清晰的因果关系,仅仅依靠这样的“松散协作”,系统真的还能保持秩序吗?这些看起来各自为政的对象,真的能组合出一个可靠、可预测的世界吗?

答案是可以的。虽然单个对象本身并不具备全局意识,但一旦我们为它们制定清晰的规则,并按照这些规则将合适的对象组织成特定的结构,这个结构就会具备明确的能力。当外部事件触发这些对象协同工作时,它们就会按照规则产生预期的行为,从而实现系统功能。

世界本身就是这样运行的。平时,每个对象看起来互不相干;但当我们按照规则把零散的汽车零件组装成一辆完整的汽车之后,只需要踩下刹车,所有部件便会协同工作,让汽车稳定而准确地停下来。

图1.3 对象组装

只要符合既定的规则和接口要求,零件本身就是可以替换的。它可以是钢制的,也可以是合金制的;可以来自 A 工厂,也可以来自 B 工厂。对系统来说,这些差异并不重要,重要的是它们是否遵守了同一套规则。正是这种可替换性,为系统带来了极大的灵活性和扩展能力。

如果我们把视野再放大一些,就会发现:按照不同层次的规则,这些零件可以先被组装成发动机、变速器、底盘等部件,而这些部件再进一步组合,最终形成一辆完整的汽车。

这正体现了面向对象中一个非常重要的思想——抽象层次。站在“汽车”这个抽象层次上,我们关注的是发动机、变速器和底盘是否协同工作;站在“发动机”这个层次上,我们关心的是汽缸、活塞等核心部件;而在更低的层次,我们又可以把活塞拆解为拉杆、曲轴等更细粒度的零件。这样的抽象层次可以不断向下延伸。

抽象层次的最大价值在于:无论站在哪一个层级,我们面对的始终都是有限的复杂度和清晰的结构,从而可以专注于当前层级的职责和行为,而不必被整体系统的复杂性淹没。

更重要的是,良好的抽象能够有效隔离变化。低层次的零件即使发生替换,也不会影响高层次的功能表现。比如,更换发动机中的火花塞,并不会改变汽车“可以正常行驶”这一整体能力。这正是系统能够长期演进而不崩溃的关键所在。

面对对象的困难

当我们开始用面向对象的方式去建模世界时,往往会遇到三个绕不开的问题。

第一,为什么要这样抽象?
现实世界和对象世界看起来差别巨大,同一件事可以有无数种抽象方式。那我们为什么选择“这种”对象划分,而不是“另一种”?什么才是合理的抽象,而什么只是人为制造的复杂度?

第二,如何判断对象的组合是否正确?
对象世界本身非常灵活,对象之间可以被任意组合。但问题是,哪些组合才能真实地反映业务需求?哪些组合是清晰、可维护的,而哪些只是勉强能跑、却注定会失控的设计?

第三,如何理解一个对象结构所表达的含义?
如果暂时抛开现实世界,只面对一组对象及它们之间的关系,我们该如何读懂它们“想表达什么业务”?如何仅通过对象和交互方式,就理解系统的目标和行为?

正是这三个问题,决定了面向对象建模不是一门“语法技巧”,而是一种理解世界、表达复杂系统的能力。

换句话说,好的面向对象设计,本质上是在回答:为什么这样抽象、怎样组合才合理、以及如何让结构本身具备可读性和表达力。

在实际工作中,我们经常会为了满足一个需求,设计出一堆类和方法。但如果追问一句:为什么要这样设计?
为什么是五个类,而不是七个?
为什么是十个方法,而不是十二个?

能把这些问题说清楚的人并不多。更多时候,答案只有一个——“凭经验”。经验当然重要,但经验也有它的不可靠性。换个说法,“凭经验”往往就是“拍脑袋”。

于是,从需求到设计、从现实到对象的过程中,那些类仿佛是凭空冒出来的:设计师灵光一现,类就有了;而经验不足的设计师,在面对复杂需求时,只能不断试错——先设计几个类,拼一拼、凑一凑,发现不行就推倒重来。很多项目,正是在这样的反复尝试中逐渐变得脆弱不堪。

我们也经常听到这样的说法:这个设计用了某某设计模式,结构足够灵活,扩展性也很强,应该能满足需求。但遗憾的是,这些说法往往停留在结论层面,却拿不出一条清晰、可复盘的推导路径,说明“为什么这样设计一定是对的”。

从根本上说,这并不是因为“世界不是由对象组成的”。恰恰相反,问题在于现实世界与对象世界之间,始终隔着一道鸿沟,而这道鸿沟的名字叫作抽象。

抽象是面向对象的核心价值所在,同时也是它最困难的地方。要真正跨越这道鸿沟,我们至少需要解决三个问题:

  • 如何把现实世界的概念和规则,系统性地映射到对象世界中;

  • 如何反过来,通过对象结构和交互,清晰地表达现实世界的业务含义;

  • 以及,如何验证对象世界中的行为,是否真实、准确地反映了现实世界的运行方式。

只有回答了这三个问题,面向对象设计才不再是“靠感觉”,而是一个可以被理解、被推导、被验证的过程。

赞(0)
未经允许不得转载:171主机测评 » 面向过程还是面向对象
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址