面向对象编程的正确姿势


本文来自作者 云鹏  GitChat 上分享 「我的10个读书策略」

编辑 | Mc Jin

世界新生伊始,许多事物还没有名字,提到的时候尚需用手指指点点。

——《百年孤独》

面向过程程序设计

计算机程序 = 数据结构 + 算法。

这是大学 C 语言教材里非常经典的一句话。这也道出了计算机程序的本质,即通过对一定的数据结构用相应的算法(逻辑)进行处理从而解决用户的问题。

这便是 C 之类的过程式语言站在计算机的角度看待编程及软件的经典视角。如下图。


这种基于计算机(实现)视角的软件开发过程通常是这样的:

 · 首先梳理业务数据(各种表格、单据、报表等),然后设计数据结构并用代码对其进行表示;

 · 接下来分析在真实的业务中对于每一类业务数据需要进行怎样的处理;

 · 最后将复杂的处理过程拆解成一个个相对简单的业务步骤并用函数对其进行程序化实现。

这样便开发出了满足用户需求的软件。

这种方法的好处显而易见,首先直观明了——用计算机解决现实世界的问题当然要沿着计算机的套路来;

其次对于一些业务逻辑不是很复杂的应用,这种方式能提供最简洁的解决方案;

但对于业务逻辑比较复杂的项目,这种方式会导致“低内聚,高耦合”的不良代码。

但这种方法是如此符合直觉,以至于今天很多使用 Java 或 Python 等面向对象语言的程序员还在用这种过程式思维在写代码。

只不过 C 中的结构体变成了 Java 中的“贫血类”(只有属性和 get、set 方法的类),C 中的函数变成了 Java 中的静态方法或无状态类(没有属性的类)方法。

为什么会这样呢?因为大家还是站在实现的角度看待面向对象。比如某著名的 Java 教程上对类的定义——“类是具有相同特性和行为的对象集合”。

这便是典型的站在实现的角度看待类的方式。那类到底应该是什么?怎样才是面向对象编程的正确姿势呢?

面向对象程序设计

我们进行软件开发时需要不停地在两个空间中转换角色——问题空间(包括用户角色、使用场景和具体需求)和解决方案空间(通过组合各种软件组件来满足用户需求)。

面向过程方法的思路是站在解决方案空间遥望问题空间,通过将问题空间中的数据和处理流程分别映射进解决方案空间来解决问题。

而面向对象则恰恰相反,它鼓励我们立足问题空间,理解需求涉及的各种概念及其职责,然后通过对象和类对其进行表达从而形成解决方案。

按照面向对象的本意,对象描述了问题领域中的某个概念并具有一定的职责。如何理解这句话呢?


其实我们日常生活中的世界便是面向对象的。这也是面向对象技术更擅长表达问题空间的原因。

举个例子,看下面某公司的组织架构图。我们看到了是 HR 部,财务部,行政部和软件开发部等,每个部门都有自己的职责范围。


部门有这么几个特点:

  1. 提供一定的职责:比如 HR 部门拥有绩效考核,员工技能提升,新人招聘等职责;

  2. 自治:比如当 HR 部门接到招聘新人的请求后,会安排部门内部相关人员通过一定的途径从社会上进行招聘。至于安排什么人通过什么途径去招聘,请求发起方统统不用管;

  3. (从实现层面来说)拥有资源和流程,这是履行职责的必要条件。

在面向对象编程中我们设计的类和对象也应该具有这三个特点。在一个具体的项目中,我们该如何定义一个类呢?

很多面向对象设计的书籍给出的方法是在项目的需求描述中寻找名字。

但这种方法存在的问题是对于业务逻辑很复杂的项目,这些名词只能反映浅层的领域知识。

如果想提炼出合理的领域概念,从而设计出一个良好的领域模型,通常需要通过多轮迭代精炼才能做到。

而这也是《领域驱动设计》这本书里作者花费了大量篇幅想要告诉我们的。

如作者所担心的,很多程序员的问题是将大部分精力都投入在研究技术本身,而忽略了问题领域本身。

而后者才是《人月神话》中所谓的软件开发中的根本的困难。

作者说到“我认为软件开发中困难的部分是规格说明,设计和测试这些概念上的结构,而不是对概念进行表达和对实现逼真程度进行验证。”让我们从发现概念开始。

发现概念

如果你对准确认识问题领域中的概念及其相互关系的困难程度还不以为然的话,让我们再来看一个例子。

下面是我在巴黎拍的一幅照片,从中你看到了什么?

头顶的蓝天和云朵,远处的树木,中间的方尖碑和下面的栏杆等等。我们能轻易识别出的概念有具体的蓝天,云朵,树木,方尖碑和栏杆,同时我们也能识别出更为抽象的空间和距离等概念。

这一切是不是很平常?其实一点都不。让我们来看《火星上的人类学家》里提到的一个故事。

维吉自幼失明,50岁时哈姆林医生为他做了白内障摘除手术从而得以重见光明。然而,

在他初看见的那一刻,他完全不了解自己看见了什么。他看见光、动作与颜色全混杂在一起,都毫无意义,只是一片模糊。

后来在一片混沌之中,他听见一个声音说:“怎么样?” 那时他才终于明白这一片光与阴影的混合是一张脸,而且正是他主治医生的脸。

作者又写道,

我们一般人生来就看得见东西,根本无法想象这种混乱的情形。对我们来说,生来就有的五种知觉,正常而且相互辅助,一开始就建立起一个视觉世界,对所见的事物有充分的概念,也明白其意义

我们每天一早张开眼睛所看到的世界,正是我们穷尽一生学习去看的世界。世界并非就这样送到我们眼前,而是我们通过不断的经验积累、分类、记忆与链接而创造出来的。

但是维吉张开眼睛的时候,他已经失明了45年,他没有视觉记忆来协助他看懂东西,没有一个经验世界与意义世界等待着他。

他的视觉经验只比初生婴儿多一点,而即使是这样,那些经验也早已被忘却。他是看见了,但是他所看见的东西却没有连贯性,不具任何意义。

他的视网膜与视觉神经十分活泼,传送着刺激,但他的脑子却理不出其中的意义。 这正是神经学家所说的,辨识不能(Agnosic)。

也就是说手术后的维吉“视而不见”,在他的视野里的只是一片混乱。其实我们所有人刚出生时都是这样的,我们用尽一生“通过不断的经验积累、分类、记忆与链接”从而在混乱中发现概念,建立秩序。

其实当我们作为系统分析师或程序员,刚接触一个陌生的业务领域时又何曾不是如此呢?

一开始我们面对的只是杂乱的数据和复杂的流程,而我们需要做的便是从混乱中发现概念,定义职责,和建立秩序

一些对于正常人来说非常平常的图像概念,比如树,方尖碑和人,对于刚恢复视觉的维吉来说却是非常大的挑战。同样一些在领域专家看来非常平常和普通的概念,对程序员来说却是重重困惑。

这就造成了程序员和领域专家的沟通有时非常困难,因为一句平常的业务行话背后其实包含了很多隐含假设和深层概念,而领域专家并没有意识到程序员其实没有这些背景知识。

就如在维吉还没有空间和距离的概念时,你让他理解园艺布局的艺术几乎是不可能的。而挖掘出这些隐含假设和深层概念才是设计出一个优良的领域模型的关键。

如何做到呢?DDD 给出的方法是基于统一语言(Ubiquitous Language)与领域专家持续交流,通过不断的重构精炼,最终形成挖掘出深层模型。

如何保证领域专家在项目中的参与度呢?Scrum 给出的方法是在项目团队中引入 Product Owner,作为客户需求的代表深度介入进项目。

但这并不是说领域专家就是发现概念的权威,这里强调的是程序员与领域专家的互动与沟通。

因为有时候,领域专家对需求某些方面的理解也是片面或局限的,只有通过双方的反复沟通与碰撞,才能加深彼此对问题领域的理解。

在发现了一个概念后,如何确定其职责呢?

【未完】

扫码完整文章

并在读者圈与作者交流

技术杂谈 · 近期好文

穷人入门区块链指南

Java 编程之美 - 线程相关的基础知识

查询参数缓存从零到一个框架的演进

一个让京东 CTO 都参与其中的敏捷转型故事

使用 Angular2+ 开发 Markdown 编辑器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值