找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3045

积分

0

好友

413

主题
发表于 3 天前 | 查看: 16| 回复: 0

一张富有创意的手绘风格办公桌插画,展示了编程工作者的灵感场景

作为程序员,我们每天都在写代码,可能用的是 Java、TypeScript 或 C++ 这类典型的面向对象编程语言。这让我们不得不思考:是我们真的需要面向对象的特性来解决问题,还是仅仅因为大家都在用而选择了它们?

What

面向对象

在面向对象的理念里,万物皆对象。它的精髓与难点都在于抽象。简单说,面向对象的成功来自成功的抽象,而失败则源于失败的抽象。

对象之间本是孤立的,就像现实生活中的你我。只有在特定场景下进行信息交互时,它们才共同构成一个过程。好比通过这篇文章,你和我之间才建立起了作者与读者的关系。

面向过程

谈面向对象,就绕不开它的“老对手”——面向过程。面向过程认为世界是由一个个相互关联的消息流组成的,类似一种螺旋式结构。每个小系统都有明确的开始、结束和严谨的因果关系。它的设计方法强调将问题分解成小的、可重用的模块,每个模块执行特定任务。

Talk is cheap. Show me the code! 想想我们怎么在线购物?流程大概是:浏览商品 → 加购 → 结算。用伪代码表示就是这样:

深色背景流程图,展示在线购物系统面向过程设计的三个步骤

这个过程很符合我们平时的编码习惯,但这确实是一种典型的面向过程写法。是不是有点诧异?我们难道在用面向对象语言写着面向过程的代码?很多时候,确实如此。日常的方法封装与调用,很大一部分就是面向过程的设计。

这里并非否定面向过程,在某些场景下它反而更直观。但面向对象设计的价值究竟在哪?

即便我们常做CRUD,重复性的代码也能堆砌出庞大系统。一个系统涉及的因素太多,想要理清所有因素的因果关系并用代码完整表述,对创作者和后续维护者来说都是一场灾难:talk is cheap, code is shit!

转换一下思路,如何用面向对象的方式设计上述购物逻辑?面向对象讲求万物皆对象,那么我们可以抽象出 商品购物车付款 这几个对象。

深色背景类图,展示面向对象设计中的商品、购物车和付款类及其交互

通过这种方式,无论在哪个层次,我们都只需要面对有限的对象和复杂度,从而能专注于当前层次的工作。例如在支付层,我们可以专注其扩展性,轻松衍生出微信、支付宝、银联等多种支付方式。

How

开发者的工作本质,是将产品需求转化为可运行的系统,中间涉及产品设计、需求建模、架构设计、编码等多个步骤。

调研需求时,我们容易陷入面向过程的误区:先梳理有多少业务流程,画流程图,再找出每个关键步骤,弄清上下文传递。实际上,架构设计、编码等都属于软件开发阶段。如果在设计前就有清晰目标,后续行动会顺利很多。面对成百上千、关系错综复杂的需求,其复杂度并非线性增长。于是,一个核心问题浮现了:需求复杂度是否等于技术复杂度?

疑问图示:需求复杂度是否等于技术复杂度

面向对象编程意味着针对建模对象编写代码,它是通过描述交互对象的数据和行为来定义复杂系统的一种技术。因此,编码之前的关键一步就是:建模

ChatGPT 对建模的定义:建模是指对客观事物建立一种抽象的方法用以表征事物并获得对事物本身的理解,同时把这种理解概念化,并将这些逻辑概念组织起来,构成一种对所观察对象内部结构和工作原理便于理解的表达。
公式:静态的事物(物)+特定的条件(规则)+特定的动作(参与者的驱动)=特定的场景(事件)

当我们为需求背后的场景建模时,首要且困难的任务是决定抽象的角度。角度一旦确定,后续设计便能顺理成章,而非杂乱无章。这也正是它与面向过程的主要区别:

  • 面向过程:希望通盘考虑,理清所有因素的因果关系。这容易让结构变得异常复杂。(把事情复杂化
  • 面向对象:希望通过合理的抽象角度,将复杂事物分解成小块,单独思考每个块,最后在特定场景下将块串联起来。(把事情简单化

因此,面向对象的关键在于,初期不要试图全盘考虑问题,而是找出问题领域内的抽象角度。只要角度找对找全,复杂问题就能被层层分解。当然,不同抽象角度之间可能是互不关联的。

做需求时,首要目标不是弄清楚业务如何一步步完成,而是要弄清楚有多少业务参与者,以及每个参与者的目标是什么。参与者的目标,就是你需要的抽象角度。

抽象角度

抽象的层次越高,具体信息越少,概括能力越强。
多形态牛的示意图,从写实到骨架线稿,形象展示抽象层次的变化

抽象主要有两种方式:

  • 自顶向下:适用于从头开始认识事物。先宏观,后微观
  • 自底向上:适用于在实践中改进和提高认识。先微观,后宏观

映射到软件开发,我们常采用自顶向下的方式:先用少量粗粒度概念搭建框架覆盖需求,再逐步细化、降低抽象层次。同时,在细化过程中,也能通过细节的共性来改进高层次的抽象。因此,二者应是相辅相成、迭代进行的关系。

迭代示意图:自顶向下与自底向上通过“迭代”循环连接

根据抽象而来的对象应具备以下特征:

  • 对象都具有原子性:在同一抽象层次上,分析时应将对象视为不可分割的整体。
  • 对象都是可抽象的:对象参与的场景或涉及的方面越多,其抽象价值越大。
  • 对象都有层次性:抽象层次越高,描述越粗略但适应能力越广;层次越低,描述越精确但适应范围越窄。

参与者

参与者在建模过程中处于核心地位。他们处于系统范围之外,但在业务范围之内,与系统进行交互。

参与者与系统边界关系图:业务范围与参与者指向系统范围

参与者和系统之间有明确边界,参与者只能在边界之外。如果参与者侵入了系统边界之内,其角色定义就值得怀疑,需要重新审视。在业务设计阶段,必须坚守这条边界,参与者过早侵入可能损害系统设计。

参与者简单定义如下:

  • 谁将使用此功能?
  • 谁对某个特定功能感兴趣?
  • 谁负责支持和维护系统?
    (参与者一定是直接且主动地向系统发出动作并获得反馈的,否则就不是参与者。)

在实际业务分析中,需区分 业务范围系统范围

  • 业务范围:项目涉及的全部客户业务领域,无论是否由系统实现,这些业务都客观存在。
  • 系统范围:软件系统将要实现的那部分功能,它是业务范围的一个子集

如果在业务分析阶段就预设了计算机系统的存在,会混淆现有业务与待开发业务,可能削弱现有业务属性,忽略其真实含义。这也是开发者对接需求时的一大误区:喜欢从计算机角度思考,一上来就讨论如何实现,并期望客户以此确认需求。其危害在于:

  1. 客户可能不理解未来的实现,但出于信任而含糊肯定。
  2. 开发者过早加入主观假设,未能真正理解客户的实际业务。

用例

用例是一种描述系统如何与外部用户或其他系统交互的技术工具。它从用户角度描述系统的需求和使用场景。简言之:用例用来捕捉功能性需求

在软件开发阶段,我们以用例为最小指导单元进行设计。标准用例应具备:

  • 用例是相对独立的。
  • 用例的执行结果对参与者而言是可观测、有意义的。
    用例的粒度不由步骤多少判断,而在于它是否能完整说明一件事。

用例并非来源于开发人员,开发人员只是用例的“翻译者”。用例的定义由参与者驱动,这也对应了其上述特征。因此,用例的来源是参与者对系统的期望。发现用例的前提,就是发现参与者,同时也就确定了系统边界。

不理解业务就写代码,如同盲人摸象。 与参与者沟通时,常感觉需求飘忽不定,好像每个点都是核心。但有没有想过:参与者“想做”和“要做”的事,可能只是达成目标的步骤,而非真实目标本身。

  • 一个明确有效的目标才是用例的来源。
  • 一个真实的目标应完备地表达参与者的期望。
  • 一个有效的目标应在系统边界内,由参与者发动,并产生明确后果。

软件开发哪个阶段最耗时?通常是需求分析与设计。为了“缩短”周期而压缩这两个阶段的时间,结果可能离真正的用例越来越远!要平衡时间与质量,就需要充分理解用户需求。当访谈不顺利时,应重新调整策略,例如调整系统边界规模或更换访谈对象。

用例不是功能点。 许多开发者误以为用例就是一个功能点,实则不然。功能的生命周期是:输入->计算->输出,功能本身是孤立的,脱离使用者的愿望而存在。

例如,一个判断用户操作权限的方法本身是脱离业务的。它的用例场景可能是“用户提交某模块的变更记录”,此时才需要校验权限,而校验只是其中的一个功能。

实际上,应从使用者视角描述:一个用例是参与者如何使用系统并获得结果的集合。换言之,用例是为完成特定目标而组合的一系列功能,这些功能在不同应用场景下可以重组,构成新的用例。

步骤不是目标。 一个用例是参与者对目标的期望。达成目标前可能经历许多步骤,但单个步骤无法完整反映参与者目标,因此不能作为合格的用例。错误地将步骤当作用例,会导致无法准确描述参与者如何使用系统,使整个系统的操作流程和交互细节偏离预期。

目标与步骤易混淆,在厘清之前,我们需要设置实际场景进行推演,通过场景中的问题来测试什么是参与者的真正目的。

也许,在此之前,参与者自己也不清楚真正想要的是什么!


回过头看《领域驱动设计:软件核心复杂性应对之道》带来的思想革新,其核心也是通过领域设计来分离业务复杂度与技术复杂度。复杂度或许永远无法消除,但我们可以分析并管理它。书中提到:

每个软件程序是为了执行用户的某项活动,或是满足用户的某种需求。这些用户应用软件的问题区域就是软件的领域。

再看我们讨论的面向对象,与之不乏相通之处。某种程度上,将“领域驱动设计”称为“模型驱动设计”也相当贴切。

回到开头的问题,我认为:需求复杂度 != 技术复杂度。而建模,理应成为我们管理复杂度的核心工具!

结论图示:需求复杂度不等于技术复杂度

本文的思维导图如下:
面向对象建模思维导图:从What到How,涵盖抽象角度、参与者、用例

最后以 ChatGPT 的观点结尾:
面向对象编程在与业务需求结合时展现出不凡的优势,通过将业务需求映射为对象和类的组织结构,我们能够更好地理解和管理复杂的业务逻辑。
通过面向对象的方法,我们能够将业务需求转化为具体的对象和类,从而更好地理解和模拟真实世界中的业务流程和实体。通过封装数据和行为,我们能够将复杂的业务逻辑划分为独立的对象,每个对象负责特定的功能和责任。这种模块化的设计使得我们能够更好地理解和管理业务需求,同时也为将来的扩展和修改提供了便利。

希望这篇关于面向对象与业务建模的探讨,能为你带来一些启发。如果你想与更多开发者交流此类心得,欢迎来 云栈社区 一起探讨。




上一篇:Redis核心原理与实践:从数据结构到高可用架构深度解析
下一篇:从理论到实践:深入解析RPC核心原理与微服务中的实现架构
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 20:21 , Processed in 0.566607 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表