GRASP

设计模式的前世今生

什么是设计模式

设计模式 其实早已是烂大街的词了, 你媳妇听到了也很可能问你一句:"设计模式?你们一群电子屎壳郎, 天天的在键盘上爬代码屎山还不够?怎么跟搞装修的似的?".

以上虽然是个笑话, 也表现出来在实际的工作中大家对于设计模式的态度, 哪怕到不了欲拒还迎, 敬而远之的程度, 起码也会认为是个比较难以掌握的技能. 以下有两种学习的情况:

  • 每次鼓起勇气去接触和学习(带着畏惧又抵触的心态). 同时又选了一些晦涩难懂的资料去学习, 被各种概念名词轰得七荤八素, 头昏眼花, 学习过程体验还不如当电子屎壳郎, 只好草草放弃.
  • 另一种情况, 平常做crud的时候, 用的都是成熟的开发框架, 大部分时间只需要遵从框架要求的开发规范即可(黏贴复制), 稍微高级点儿的编程技术压根就用不上, 长此以往, 别说设计模式, 面向对象的三大特征都忘了, 如果不是为了面试, 都懒得看一看设计模式每个模式都叫啥, 更不要说思考每个模式的适用场景了.

设计模式说白了就是解决问题的方式方法(方法论). 约会要AA, 打游戏要4090, 灯泡不能塞嘴里等等都是日常非常典型的生活模式. 设计模式也是同样的简单概念,

生活中遇到问题后, 先辈们为了解决问题找出了应对方法, 久而久之得到了认可和推广, 总结为解决这类问题的处理模式.

那么到了研发体系中, 研发过程中碰到的99%的问题(太阳底下没有新鲜事), 我们的前辈们都在无数的项目中解决过无数次了, 总有高手把之前遇到的问题及其解决方案进行归纳总结, 把编程过程中最经常碰到问题的解决思路提炼出来, 作为结论整理成所谓的设计模式.(但是很多问题我们碰不到, 也就没办法理解问题到结论的推导过程, 直接被灌输了结论, 理解起来确实存在难度).

至此, 大家不要把设计模式想得太高大上. 相反, 它就存在于我们的日常工作中, 因为它本身就是为解决我们日常工作中遇到的问题而存在的. 在我们试图寻找解决某些需求问题的设计方案时, 殊不知, 我们当前所面临的所有难题, 在业界上早就有无数的先辈给出了成熟的方案. 你学或不学, 它就在那里, 等待你发现它的好(或等你重复发明轮子后还不如它之后嘲笑你). 大部分的设计模式, 其实都非常简单, 看过之后甚至会嗤之以鼻: "这破玩意儿也好意思归纳成一个模式, 我都用了多少年了".

那么,为什么要加上 "设计" 这两个字呢?为什么不直接叫 "编程模式" 呢?要回答这个问题,咱们还得回到面向对象。我们使用面向对象思想进行工作的时候,尤其是进行复杂业务逻辑产品开发的时候,要经过以下几个大的阶段:

项目立项 - 获取需求 - 需求分析 - 系统分析 - 系统设计 - 程序开发 - 测试 - 项目交付

当然在这个基本流程中, 其中某些过程可能会发生反复和迭代, 但这不是我们本次讨论的重点, 我们重点看 "分析" 和 "设计", 对应于我们面向对象语言, 我们有 OOA(Object Oriented Analysis) 和 OOD (Object Oriented Design), 说到这里我想大家应该明白设计二字是从何而来了. 是的, 设计模式是编程思想, 是别人的先验之经验, 设计模式主要就是在 OOD 面向对象系统设计这个阶段使用的. 不要简单的认为这个阶段就要大规模敲代码了, 对于复杂的系统来说, 这部分消耗的时间和精力要高于程序开发阶段的. 这个阶段出了问题, 后边参与的人再牛X也只是徒劳, 他们一边吐槽一边想着人和代码有一个能跑就行.

额外提一句

对于很多新手或没有接触过大型项目的人, 安排给他们的工作都是被放在被设计好的位置上开发或者维护已经设计好的模块或者小的功能点上, 基本不可能看到整个项目是如何从 0 开始逐步成形的, 最悲剧的就是工作多年后, 依旧没有接触到更高一级的东西, 人会越来越没有信心. 如果长时间的工作经历让一个人在专业领域里面没有足够的积累和筹码支撑的话(这就是面试时常常碰到的一年经验干了十年), 对个人来说这人心态就崩了, 职业生涯就到此为止了. 所以衷心建议趁年轻, 多学点东西, 总是好的, 哪怕现阶段甚至未来两三年也用不上也不能成为不学习的理由. 当我们只能看到 CRUD 的时候, 我们也只能学会 CRUD, 最终也只能成为CRUDer. 说一句特别特别俗套的话: "不要用一个锤子去解决你遇到的所有问题".

软件工程绝不只是一帮人在玩理论炒概念, 是因为软件开发中需要解决具体的技术问题, 同时还叠加了世界上最为复杂的人类问题和社会问题, 我认为软件工程本质上就是在从OOD的角度对社会生产生活中的问题进行抽象处理并给出设计模式的过程, 这话说得太形而上学了, 反正就这么个意思吧. 《人月神话》之所以长胜不衰, 是因为其中所讨论的问题永远也不会找到解决方法, 不是解决不了技术问题, 而是解决不了人和如何组织人的问题.

说这么多希望大家已经知道什么是设计模式了, 后面我们不光要知道什么是设计模式, 还要知道它在项目中的哪一个阶段应用. 我们一定要培养面向对象的思维, 而不简单的停留在代码这个维度, 代码只是重点解决了 程序开发 阶段的问题, 整个项目其实是一个严格推导的过程, 一个项目中, 应该定义哪些类, 定义哪些接口, 设计哪些模块, 如何分层, 都是推导出来的. 那上来就给几个破图, 啥也别问, 照着干就完了的工作方式, 不是在解决问题, 而是在生产 Bug, 在给后人挖坑, 给事业埋雷.

学会设计模式有哪些好处?

我们说说学会设计模式有哪些好处. 那么这里就有一个非常重要的前提: 学会, 到底怎么才算是学会呢? "学会" 的标准又是什么呢?不要笑, 这真的是一个值得考虑的问题, 这绝对也是大部分初级面向对象技术人员都会面对的一个问题.

我们都知道所有的设计模式都基于面向对象的理论或者说基本思想实现的. 因此首先你面向对象的基础砸得要足够坚实, 对面向对象的编程思想要有足够得认识和深入些的理解, 而不是靠记忆背出来的面向对象, 这一点很重要. 一些人员看了些书之后会说: "我知道什么是CQRS了, 我知道什么是工厂模式了, 我知道什么是继承多态了......." .

"我知道"绝大多数情况下都是谎话, 要么是我们觉得自己知道了, 要么是让别人以为自己知道了, 在真正的实践中, 我们其实是不知道的, 很可能只是我们记住了一点点东西而已, 然后装知道. 就像考试做错的题, 媳妇让你洗的衣服, 讲了又讲, 反反复复, 你好像是知道了, 无论给你多少次机会, 还是会做错, 明明 "我知道" , 可怎么就还是不会呢?有的东西不是靠记忆的, 要看实践, 不能只背不悟不实践. 在此搬出领袖的一句话:"实践是检验真理的唯一标准". 附上我刚入行时, 我的总监告诉我的一句话:"不要聊也不要看, 自己动手做一遍就会了".

这句话很土, 但话糙理不糙, 很适合咱们现在讨论的问题. 很多学习者没有掌握学习设计模式的正确姿势. 什么问题呢? 设计模式对应的问题场景的识别, 也就是案发现场的分析问题. 如果连案发现场是怎么一回事都不知道, 上来就要直接破案, 那就是乱搞胡猜嘛.

每一个设计模式是为解决某一类问题而制定的解决方案, 能否识别出问题场景对于"学会"至关重要, 而大部分人在学习的时候, 很多都是走马观花看看概念, 然后就直奔代码实现而去(神马DDD没有能够落地代码框架, 那老子学个屁). 这样的学习者只想看尽快搞懂代码, 学会几个名词, 然后自我满足得陶醉一下, 回头可以吹个牛逼. 至于为什么要这么解决问题, 解决了什么问题, 不这么做会有哪些风险? 他很难回答上来. 例如: 我们看别人滑雪的时候, 就简单的几个动作而已, 而我们自己到了雪上才会真正体会到我们需要克服的困难太多了, 首先如何保证自己不恐惧, 保障自己不摔倒, 保障摔倒的姿势够帅等等都是基本的要解决的问题, 而以上只是开放的雪场里学滑雪这个场景你会遇到的问题. 珠穆朗玛峰, 南极北极, 又是不同的场景, 存在不同问题, 而你的应对之法则会完全不同, 你的"设计模式"也会完全不同.

我们总是在说复用, 复用公共组件, 复用前端组件, 我们复用了太多代码层面的东西. 设计模式能够让我们复用(复制)别人的解决方案, 复用(借鉴)别人的编程思想和先进经验, 等到自己能够真正掌握, 就会"顿悟", 就可以做到一个打十个, 任何业务难题, 任何技术场景到你手里都能迎刃而解. 反之, 你将永远也无法进入到下一个层次, 在这个行业中, 始终停留在初级阶段, 就会被后浪拍死(这就是常说的搞技术的年龄上限), 留给失败者的只有被淘汰的痛苦伴随一生.

设计模式间的区别

GRASP 是 General Responsibility Assignment Software Patterns 的缩写, 江湖上的兄弟们比较流行的叫法是"职责分配模式". 虽然最后一个单词是 Patterns, 但是等把 GRASP 所有的东西学完之后, 你会发觉 "模式" 这个说法不怎么恰当, 与其说是模式, 不如说是一些原则和指南. GoF 23 那一堆东西才是严格意义上的模式, 而且是系统设计阶段采用 OOD 方式的设计模式.

主要看一下 职责分配 这四个字, 这四个字才是 GRASP 的精华所在, 我们现在需要把 "职责分配" 在项目的生命周期中做一个基本的定位:

项目立项 - 获取需求 - 需求分析 - 系统分析 - 系统设计 - 程序开发 - 测试 - 项目交付

职责分配发生在哪个阶段呢, 需求调研、需求分析、系统设计 这三个阶段. 在需求调研这个阶段, 我们会得到系统中的责任人(有时也叫干系人) , 责任人/干系人这个专业词语最早出现在项目管理学中, 不要畏惧新名词儿, 都是纸老虎. 比如项目上出现了问题, 我们总说某个人在这件事上逃不了责任, 责任人的"责任"跟这里的责任其实是一个意思. 责任人就是使用我们软件产品的所有人(或角色), 比如用户, 系统的管理员, 运营人员, 老板, 老板娘等等. 除了责任人, 我们还会在这个阶段得到 责任事 (也可能是在需求分析阶段分析出来的), 当然这是我从责任人发明的新词, 意思就是相关责任人在软件中负责的功能. 不管是人还是功能或者说业务, 它们都需要进行 "职责分配" , 而职责分配不是拍脑袋乱来的, 这里面是有套路在的. 不同的人有不同的权限和责任, 不同的业务有不同的规则和流程, 哪怕是相同的业务因为责任人不同也会变化规则, 这话说起来圈圈绕绕的, 其实道理很简单, 普通玩家和人民币玩家永远都不是一个物种. 在现实世界中 "人"、"事"、"职"、"责" 是怎么回事, 到了软件系统中, 也是那么回事, 我们不过就是把现实世界的规则通过逻辑进行了数字化. 只有分析清楚了 "人"、"事", "职"、"责" 之后, 才能谈到 职责分配.

以上内容搞清楚后, 才能进行 系统设计程序开发. 大家都应该知道, 业内失败的项目占所有项目的比例其实是非常高的, 很多软件项目其实还没有开始的时候就注定会失败, 因为 "职责分配" 是超出软件开发范畴的, 如果一家公司想把某条业务线信息化, 那这家公司内部本身的 "人","事","职","责" 等等必须都是清楚明白,且执行到位的才行,如果它自己内部本身就是一团浆糊,人浮于事,人与人之间,部门与部门之前,职位与职位之间界限不明,责任不清,遇事推责揽功,宫斗不断,就不要指望这类公司的项目能做成,必定是失败的项目. 而很多无良的软件公司通常喜欢这样的项目,因为项目失败的 "责" 不在自己身上,只要配合这类公司的项目负责人"演戏",任由他们修改需求,把项目周期无限制的拖下去即可.

最讽刺的是

技术人员即便在这种混乱的项目中也能把 "职责分配" 做好,因为技术层面上,会排除具体人的因素,而是以 角色,流程,规则 等等逻辑方式去解决问题,摒弃了人情世故,鸡鸣狗盗,世态炎凉的社会化问题,因此这类项目也能完成和交付. 至于交付之后,他们用不用,能不能坚持用,就是另外一回事儿了,但是这种项目哪怕交付了,这也不能算是成功的项目.

在逻辑层面上,GRASP 着重考虑的就是 "对象" 的 设计原则 及其 职责分配,而 GoF 设计模式则主要考虑 OOD 阶段设计如何实现,对象与对象之间如何交互,程序结构如何更加健壮,易扩展,易维护. GRASP 是 GoF 设计模式的前提,GoF 设计模式就是符合 GRASP 模式原则要求的面向对象的设计模式. GRASP 是独孤九剑的心法总纲, 是路线规划方案, GoF 就是具体的剑法招式, 是根据规划制定好的具体可实施的路线了. 这是它们之间最根本的区别.

GRASP核心概念详解

General — GRASP 的原则是通用的、广泛适用

GRASP 所说的良好的职责分配原则是放之四海而皆准的,并不狭隘的限于软件行业内,这也是为什么有些公司的项目还没有开始就注定要失败,因为如果公司内部本身就是权责不明,事理不清,宫闱恶斗。这样的公司要实现公司业务数字化,信息化,基本就是在浪费时间和金钱,除非先把自己内部的问题解决清楚才行。

Responsibility — 责任,职责,义务

其实严格来说,只采用单词原意 "责任" 或 "职责" 是不够的,"权责" 似乎更为妥当。程序中的实体或者功能模块应当具备那种职责,负责什么,都需要分析清楚。现实社会中,不管在家庭还是公司或者某个组织内,我们的职责就是 "维护世界和平",听着挺扯蛋是吧,但是仔细想想的话,不管是国家,政党,机构,家庭,人,我们做的所有事情无论大小不都是保障世界在现有格局和规则下平稳运行吗?所以虽然扯点蛋,但扯得力度并不大。

Assignment — 责任分配,职责安排

职责如何用更为合适的方式分配给指定的类或者模块是 GRASP 最核心的部分,分配职责的时候有很多的策略和方案。要介绍的 9 大要素,其实就是从9 个方面的大原则。根据这 9 个大原则,我们就可以将职责和负责这个职责的对象(模块|系统)更好的组织在一起,为以后的程序设计阶段打下坚实的基础。

Software — 软件

这个就不用说了, GRASP 本就是专门服务于软件行业的一种技术手段.

Patterns — 模式

可以说 GRASP 就是衡量一个设计模式是否是一个好的解决方案的标准. 比如分层是否合理,因为分层就是在分工,分工就是在搞 "职责分配",逻辑处理应该放在哪层?信息显示该放在哪一层?为什么现阶段大部分软件项目采用的三层架构设计(数据访问层,业务逻辑层,界面层)?这些问题的答案,咱们都可以从 GRASP 倡导的原则中找到其中的部分答案. 如果没有统一的原则作为指导, 一个公司的架构设计人员也会在刀光剑影中争论不休. GRASP就是衡量我们设计的标准.

GRASP 对于面向对象的系统分析和系统设计具有重大的指导意义,有了它咱们就能把 "什么人该在什么位置做什么事" 这个老大难的问题解决好。面向对象的设计过程就是将责任分配给对象的过程。下面把 9 个要素罗列一下:

职责到底是什么

本篇一直在说 "职责分配",那今天咱们就把 职责 讲明白,吃透了 "职责",后边的那些内容就很容易搞定了。

职责概念内 有 认知职责行为职责 这老哥们. 巧合的是, 世间最难做到的事恰恰也是 "知,行 "二字. 恰恰就是由明朝的大牛"王阳明", 得出 知行合一 的终极指南.

先说 认知职责 吧,我们常说 人贵自知,人要有自知之明,这真的很重要. 然而经常出现的场景是:"你说的道理我都懂,但是让我干我一个也不会",知行合一太难了,上嘴皮碰下嘴皮相对简单多啦(当然能讲出来也没这么容易, 不然您来讲一课试试?),人人都知道该怎么减肥, 但是要你每天5点晨跑那可真要命了。

到了程序的世界里,我们在 OOA 和 OOD 阶段,就是在分析 "对象" 要有自知之明这个事情,虽然作为人类我们很难做到 "知行合一",但是我们分析和设计好的 "对象" 却能完成我们创建者无法达成的 知行合一(现实世界虽然不完美, 但是我的代码是完美的)。

那如何做到 "自知"(认知职责)呢,那首先要对自己所拥有和管理的信息(对象的属性)有足够的认知, 这是一个分析建模的过程, 比如我们相亲时: 可以对对方建模,身高 170,体重140斤,皮肤很白,这就是一个很朴素的认知。

认知职责共有三重境界:"见天地","见众生","见自己"。看起来就不严肃啊,是我瞎编的 哈哈。你知道自己的身高,体重,性别,特长等等,这些只是认知职责中最低的境界 见自己,对应的就是面向对象中属性的概念,一个对象能够了解到自己的属性就像一个人知道自己的身高体重,这是它的本分,没啥好骄傲的,认知职责的第一重境界 "见自己" 看起来还是比较容易的哈.

然后就是第二重境界:"见众生",说的是对相关对象的认知,相关对象并不是一个难以理解的概念。之前说过,程序世界就是现实世界的映射,不光是简单的静态的事物的映射,更是社会规则关系的映射。虽然在程序世界中,我们可以突破物理规则的约束,但是突破不了人类思维模式。而说到社会规则,其实说的就是人与人之间,组织与组织之间,人与组织之间的关系, 规则和流程。在面向对象的理论中,我们同样也会定义 对象与对象之间的关系,大家可以回忆一下面向对象的关系都有哪些?

对象间关系

继承、实现、依赖、关联、聚合、组合

想到一对多, 多对一, 一对一的同学请自觉找书复习, 基础太差了哈

在这个境界我们就不光要知道本身的属性,还要能 "了解" 到关联对象。这里有直接关系,也有间接关系,但是不同"对象"之间的关系必须是清晰明确的。比如说 在某宝下了 3 个订单,那 某宝的这个账户对象就应该能 "认知" 属于自己的这些订单。而通过这 3 个订单呢,也可以知道是谁下的单,在设计对象的时候使用关联关系就能够很容易的实现这个功能了, 我相信这是大家都能够理解且日常工作中反复实践过的.

最后呢说说 "认知职责" 中最高级的境界:"见天地" . 有些相关联的事物无法直接获得,没有直接或间接的关系进行关联,但是却能够通过 某种方式方法推导或者计算出来. 你知道自己处于天地万物的哪个位置, 也可以根据天地万物的任意对象找到你,如果我们不了解自己, 也不了解身边的人和事,没有身边的人作为衡量标准,我们又如何对自己做评判和定位呢?课本上说过一只坐井观天的青蛙,当我们无法与这个世界联系的时候,就会非常悲剧的无法对社会和自己进行基本的认知,认知都出了问题,又何谈行动?

通过上面的一通废话,希望大家已经知道职责中的 认知职责 是怎么一回事了。我们解释清楚了认知职责后,行为职责就非常容易理解了,一个对象的行为必然"不是跟自己相关"就是"跟与自己关联的类对象相关",这个时候贴出官方极为晦涩的定义和描述了,对象的行为职责包括:

  • 自身执行一些行为,如创建对象或计算
  • 初始化其他对象中的动作
  • 控制和协调其他对象中的活动

最后说一句知行合一是一个很难达到的境界,真的难在"行"吗,很多情况下,可能还是因为我们无知,或者说咱们并不是真的"知",只是在自以为是而已

信息专家 Information Expert

提到专家, 这个词已经变成了不可信, 脑残的代名词. 因为那些所谓的专家经常发表的"奇谈怪论", 让我们目瞪口呆, 挑战智商和社会的底线. 那么这些专家真的是脑残吗? 他们又为什么会说出这么多脑残的发言?

答案在于 "跨界", 搞围棋的非要去讨论AI, 手机的KOL接了个特斯拉的单子, 售前去讨论机房建设. 其结果大家自然会觉得离谱.

那么问题就来了, 所谓的专家. 一个重要的标志就是 "信息" . 专家必然是在某个领域里面掌握 "信息" 最多的人, 他们拥有"信息"的质量和层次是一般人所不能比的. 讲到这里, 大家回看一下标题, 是否能对 "信息专家" 有一个大概的认知了, GRASP对于信息专家的专业表述: "如果某个类能够完整表达某方面的信息,足以实现某个责任,那它就是可以拥有这个职责的信息专家". 不知大家对于这段话的感觉是怎样, 想必有些人是要摔键盘的, 请举起键盘的哥们先等一下, 思考一下几个问题: 键盘是你的吗? 如果是公司的你有资格摔吗? 它是你的"信息"吗? 你是它的"信息专家"吗?"摔键盘"这个行为是我的"职责"还是公司的"职责"?

这些问题综合起来就是 "信息专家" 的内涵, 很多技术研发出身的人员之所以不能领悟很多技术概念, 不在于他们技术能力差, 而是太专注技术层面, 或者说太专注"代码实现"了. 而绝大部分技术问题,都可以从社会问题上找到答案. 让最专业的人干最专业的事,这是普遍被认可的一条社会效率法则,而社会分工体现的恰恰就是职责分配, 当然社会分工也会存在问题, 比如很多人分配到的职责, 但未必表现的 "专业",能够做到 "专业" 的人只有很小的比例。但是到了软件领域则不同,我们分析出来的对象它必须要是绝对的"专家",绝不能 "跨界",因为超出的部分不是它的职责,这也体现了SOLID编程原则中 "单一职责原则" 的精神,但是 "信息专家" 原则并不能简单的等同于 "单一职责原则" ,因为 "信息专家" 表达了更丰富的内容.

解释一下信息专家与单一职责

为什么说"信息专家"并不单纯指代"单一职责原则"?为什么"信息专家"的内涵更丰富?那咱们还是要回到项目的阶段中,我们之前说过,GRASP 重要在项目的 需求调研 - 需求分析 - 设计 三个阶段中起作用,但 SOLID 原则 和 GoF 设计模式 都主要在系统设计阶段使用。而需求分析和系统分析时最重要的事情就是识别出能够描述业务场景中具有关键作用的对象,并抽象出"类"。GRASP 的"信息专家"原则恰恰就是在这个过程中寻找对象并分配职责的最重要的方式方法,这项工作完成的质量好坏,直接决定了项目后续阶段能否高质量的完成。这也是为什么"信息专家"是GRASP最重要的原则,也是为什么它是排在第一位。

不止体现"专家"的概念同时"信息" 二字也非常重要,对象封装的全部 "属性" 定义了这个对象是哪方面的"专家",似乎这话还是有点绕. 讲的再直白一点, 很多情况下 "数据" 本身并不等同于信息,比如数字 250,我们不知道它代表什么,如果它是 "智商" ,那智商 250 就能够表达某一项信息(智商信息),这是一种粒度很小的信息;如果 User 对象只有这么一个属性数据,那它还不能"完整且充分"得表达出这是一个人信息。我们现在要得是 "某一方面" 的信息,但是我们可以翻译一下 "某一方面" 为 "必要属性"。"一个智商 250 的名叫王大牛的超级帅哥产品"这表达出来的就是完整的描述一个人 "这一方面" 的信息。那我们可以抽象出一个类 User,它在当前的这个系统中,必须要负责管理的数据就是 用户身高(250),用户性别(男),用户姓名(王大牛),用户职业(产品),用户技能水平(超级帅)。那其他的数据不要了吗?比如出生年月,手机号码,电子邮件,如果这些数据项当前的项目完全用不到,那它们就不是必要的属性,"用户"这个"信息专家"也就没有"职责"去管理这些信息并为它们劳心劳力。

而到了能管系统中,企业的名称,营业执照,营业范围,设备档案等则会成为"传递出这具体是一个什么样企业这个关键信息的"必要属性。然后企业,设备,场景等等是咱们比较容易想到的能管系统中的信息专家,它们职责分明,分工合作,共同构建了"能管系统"的基础。设备就是负责设备信息的,不能闲的没事去修改企业封装的数据,企业也不能越俎代庖的去修改采集记录的数据,要修改的话,也得"设备"去修改,不是你的键盘,不要随便摔。

低耦合 Low Coupling

低耦合 是我们耳朵听出茧子的一个专业名词了,做不到低耦合,谈何权责明晰的职责分配,如果连你、我之间的边界都不清晰,谁都想做别人的主,那岂不是满大街都是土皇帝了?

问开发人员什么是耦合, 他们心中一定闪回了, 上回改别人的代码时, 改一行代码重构了整个项目的车祸现场, 牵一发动全身就是高耦合, 反着来就是低耦合咯

但是我们前面已经有了专事专做的信息专家了, 追求低耦合是要解决什么问题呐?

"减少因变化产生的影响"

其核心工作就是要减少 依赖性(如: 类与类之间的依赖,对象与对象之间的依赖,组件与组件之间的依赖,系统与系统之间的依赖),只要依赖性降低了,目标就达到了一半,另一半则是要提升重用性和扩展性。

面向对象编程是我们常说的装B词汇之一, 常用的java也是号称面向对象的语言, 但是真正的开发过程中大部分人采用仍然是 针对具体页面的过程式编程, 原型ui上有个什么样子的表单, 要进行crud, 设计一个一模一样的数据库表, 然后把保存和查询的过程coding出来.

这套流程很简单, 我们玩的很熟练. 但是我们应该真正的从业务出发, 提炼出业务中的不同功能模块, 体现模块核心价值的对象, 再封装标准规范的接口, 学会面向抽象的对象设计, 面向标准接口编程, 而不是拘泥于具体的原型ui.

面向对象中的 "继承"、 "组合"、"接口" 等等是实现 "低耦合" 的重要手段, 在我们日常的coding中却非常罕见, 让我常常考虑改用php是不是更好. (PHP is the best language for web programming, but what about other languages?)

无法达到低耦合的目标, 有两种情况:

  1. 程序的设计者, 没有很好的识别出业务中可变和不可变的部分. 如果要做的系统本身非常 "稳定", 系统间, 模块间, 对象和数据都可以按照一万年不变的方式运行, 绝对不会有任何变化, 那么再高的耦合度都无所谓. 这么讲其实是想说, 我们常常需要在降低耦合和封装之间进行权衡和取舍, 不太可变的部分, 耦合就耦合吧, 为了效率偶尔牺牲一些扩展性和可维护性. 但是对于大概率会变化或需求不确定的部分, 请对自己好一点, 耦合一定要低一些, 要不然晚上通宵, 周末加班的一定有你, 而且导致你通宵到天亮的凶手不是别人, Just Yourself !(这也是为什么新人或者新业务开发时加班会比较多的部分原因, 经验和实践的不足, 设计开发出来的东西只能满足当前短期的需求, 一旦出现更多的场景或变化, 程序马上就搞不定了, 需要彻底推翻重来. 我第一家单位的总监面试时最关注的一点就是: 这人一定要足够懒, 勤快人是干不好程序员的, 必须手懒脑子勤快 )
  2. 另一个情况不是技术力问题, 而是对业务理解不够透彻, 一旦出现了这样的情况, 不要折磨项目上的产品和技术人员了, 把负责业务的人拉出来, 给团队做培训, 帮我们识别可变和不可变, 把业务流程和每个阶段的规则详细讲清楚, 共同研究业务方案. 解决耦合问题不光是技术层面的事, 还有分工协作层面的, 不能因为我们是负责技术方面, 就把业务的内容忽略, 技术用的再多, 再高大上, 业务上的问题解决不了, 都是浮云

低耦合和高内聚是软件设计中的真正核心原则, 做到了 低耦合 我们的工作会轻松, 效率会增加, 带来的好处:

- <font style="color:rgb(23, 43, 77);">当前组件不受其他组件变化的影响</font>

- <font style="color:rgb(23, 43, 77);">便于理解,方便沟通</font>

- <font style="color:rgb(23, 43, 77);">易于复用,方便集成</font>

- <font style="color:rgb(23, 43, 77);">易维护, 好修改, 少加班</font>

- <font style="color:rgb(23, 43, 77);">有时间交更多男女朋友</font>

高内聚 High Cohesion

公司里有没有见过这样的人:本职是做运营的,但顺带帮领导整理PPT、统计财务数据、调研竞品、对接法务……什么都干,每件事都马马虎虎,自己累得半死,做出来的东西却没一个靠谱的。HR说这人不聚焦,领导说这人效率低,当事人自己还委屈得不行:明明付出了那么多,怎么就落得这个评价?这种人在职场里最悲催的地方在于,努力是真实的,但结果往往是越帮越忙。

程序里的低内聚模块就是这样——它不是不努力,它干的活太杂了。从程序设计的角度来说,"内聚"指的是功能内聚,是对元素职责相关性和功能集中程度的度量. 如果一个元素内(系统/模块/类)的行为职责都高度相关, 并且没有加入与元素本身不相干的职责, 就可以说该元素在它职责范围内是高内聚的. 反过来,如果一个人承担了过多不相干的工作, 尤其原本不属于他的活也要干, 工作量还特别大, 换谁也会吃不消, 不是不能做, 而是无法专注, 效率低.

现在车轱辘话又要来了,高内聚了自然就能达到低耦合的目标了,但是如果你读过前面的文章,你就会明白,高内聚只不过是实现低耦合的一个手段罢了,低耦合可不单单只是功能上的低耦合,还有架构设计时的低耦合。由于内聚性非常低的元素干了太多与自己不相关的工作,或者需要完成太多的工作(虽然都是职责内的),缺点是显而易见的:

  • 难以理解
  • 难以维护
  • 难以复用
  • 脆弱不堪

上述的这些缺点都非常容易理解,我们再举个例子 如果我们打开一个 采集 的服务,但是 采集 服务干了太多 模型 的活儿的话,你说恶心不恶心,怎么去复用它;又或者打开了一个 实时库服务(功能都内聚),一个文件 1 万行代码,梳理代码逻辑就像年会时听领导讲话,别说程序员不看,就是机器也懒得把它翻译成 1 和 0 啊。面对这样的情况,你怎么理解它,怎么复用它,怎么维护它,别说它面对变化的脆弱不堪,换成是我维护这样的程序,我心理都能被它折磨脆弱了。

所以说,"高内聚"不是简单把什么功能都内聚,而是要有策略讲方法的,首先就是 "粒度",一定是 "大粒度" 的功能内聚。我们开始设计时, 不要一开始就从头发丝一样细的去设计表结构表字段,领导天天提的几个项目流程中的几个步骤,这个时候就是它发挥作用的时候了:

  • 第一层面 系统层面 : 系统集成关系图
  • 第二层面 模块层面 : 系统模块关系图
  • 第三层面 接口层面 : 各模块接口定义与时序图

我们会发现一个问题,就是粒度不同,层面不同,我们分析和设计的时候,也要分析其中核心价值是什么,也就是它对外提供的核心服务是什么?只要把这个问题搞清楚了,让它去体现自己的核心价值就行了,比如模块有核心的服务接口向外提供服务,然后模块中只保留必要的辅助实现核心服务接口的功能即可。

与低耦合一样,在所有的设计决策期间,高内聚是要时刻高亮的原则,是一个需要不断考虑的原则。设计系统的时候,我们需要搞清楚这个系统需要跟哪些外部/三方有交互。当我们进行模块设计的时候,就需要引入更多的设计考量,以便让各个业务对象和接口发挥应有的作用,模块化就是要将系统分解成一组高内聚、低耦合的系统构件。构件这个概念在建筑行业里有,现在的建筑业越来越构件化,翻译成咱们的语言就是组件化。道理是一样的。

那么如何达成 "高内聚" 而又不把内聚做滥的目标,就看你的本事了,反正记住一句话:不要什么事都想着自己做,诸葛亮事必躬亲,但最后是心力交瘁而亡。内聚的度一定要把握好,怎么把职责合理分配好绝对是门艺术,后面要说的 "纯虚构" 和 "多态" 会帮助我们更优雅的完成高内聚和低耦合的目标。

控制器 Controller

你去政务大厅办事,进门之前你啥都不知道——谁管户籍?谁管社保?谁管工商注册?所幸大厅门口有个导办员,你跟她说你要干啥,她拿出张单子递给你:"去 8 号窗口"。你到了 8 号窗口,那个工作人员才是真正处理你业务的人。

导办员本人解决不了任何业务问题,她不给你盖章,不查你的档案,不帮你填表——但她是整个大厅的入口,是"外部请求"和"内部处理"之间的桥梁。

这就是 GRASP 里控制器的核心价值:接收外部事件,协调内部处理,自己不干活。

小饭馆还好,老板娘一个人既端菜又收钱还顺带招呼客人,一脑袋全包了,管用。但是饭馆做大了,你就必须有专门的收银员、专门的带位小哥、专门负责外卖的人——这时候门口还得有个人统一接待,告诉你"您这个需求找谁"。这个人不负责做饭,不负责收银,但她是整个运转体系的入口,缺了她一样乱。系统的控制器,就是这么个角色。

控制器的本分:只管"协调",不管"干活"

控制器这个角色,有一条雷打不动的本分:只负责协调,不亲自动手。

就像一个好的项目经理,他的职责是:弄清楚来了什么需求,确认是谁的地盘,交出去,等结果,汇报。他不亲自画原型,不亲自写代码,不亲自跑去跟甲方讨价还价。具体的事,由具体的信息专家去做。这就是控制器的工作边界。

道理简单,但现实里很容易失守。

那个"什么都管的领导"

见过那种领导吗?不管大事小事都要经过他,开会要他,签字要他,请假要他,买盒打印纸也来找他,连下属跟甲方发的每封邮件他都要改一遍。一开始大家觉得这领导很负责任,但渐渐就会发现:这个人变成了整个团队最大的瓶颈——他一休假团队立刻瘫痪,他一回来面对的是一摞摞积压的待处理事项,他的下属也越来越不会独立思考,因为"反正领导要改"。

这种领导犯的错误,不是他不努力,而是他混淆了两件事:"负责这件事有没有做好""亲自去做这件事"。前者是协调职责,后者是执行职责,两个是完全不同的角色。

系统里的控制器一旦开始"亲自干活"——自己计算业务数据,自己处理各种规则,自己管理各种细节——就变成了这种领导。它变得越来越庞大,越来越难改,动哪里都牵一发动全身,最后谁都不敢碰它。

一个好的控制器应该轻装上阵,它的字典里只有四个动作:接收、判断找谁、转交、汇报结果。剩下的,都是别人的事。

控制器是乐队指挥,不是演奏者。指挥的价值在于让所有人在对的时间做对的事,而不是自己把所有乐器都抢过来独奏。毕竟,一个优雅的指挥家,总是比一个到处乱插手的领导,更能赢得掌声。

多态 Polymorphism

同样是"项目经理"这个头衔,公司里的张三靠严格的甘特图和里程碑节点推进项目,李四靠大哥情怀和饭局维系团队,王五靠"不管发生什么先加班"的铁腕纪律赶进度。三个人的岗位名称一样,领导问"项目进展怎么样",他们都能给出一个答案。但背后的实现逻辑,天差地别。

这就是多态最朴素的含义:相同的接口,不同的实现

大部分人对多态的理解停留在"父类引用指向子类对象"这个语法层面,背得很溜,考试没问题,但面对真实的设计问题时依然两眼一抹黑。GRASP 对多态的表述更有指导意义:

当行为因类型而不同时,把不同类型相关的职责分配给该类型自己,而不是靠外部判断来代劳。

换个说法:不要有个人替所有类型发言,让每个类型自己说话。

同一件事,不同角色,处理方式不同

公司收到一封客户投诉,这封信转到不同部门,结果大相径庭:转给法务,他们发律师函;转给客服,他们打电话安抚道歉;转给技术,他们排查问题修复上线;转给公关,他们出一篇声明稿。同一件事——"处理这个投诉"——每个部门用自己的方式响应。

现在问题来了:如果有个专门"分发投诉"的人,他不了解每个部门内部是怎么处理的,他只需要知道"这个投诉该找谁",然后交出去就行。他不需要知道法务是怎么写律师函的,也不需要知道技术是怎么查 bug 的。这种"分发方"和"处理方"的分离,就是多态带来的好处。

反过来,如果没有这种安排,每次来了投诉,分发方就得先判断:是法务处理?还是客服处理?还是技术处理?每增加一个部门,这段判断就要改一次,判断错了后果自负。时间长了,这个"分发方"就变成了整个流程最难维护的节点——因为他知道的太多,所有人的内部逻辑都在他脑子里,他一走什么都乱了。

多态要解决的,就是这个问题:让每种类型自己负责自己的行为,而不是让一个外部角色替所有类型做决定。每个部门是自己业务的信息专家,它自己知道收到投诉该怎么处理,用不着别人替它操心内部流程,这也是高内聚的体现。

什么时候用多态,什么时候直接判断就好?

不是所有"类型不同、处理不同"的场景都要用多态,有时候直接判断更清楚、更省事。

就像公司就两个部门,来了事情你自己判断一下给谁处理,花五秒钟。但公司扩张到二十个部门,还要继续扩,你每次来一件事都要改一遍判断逻辑,这时候不如建立一个统一规则:每个部门自己登记"我处理什么类型的事",来了事情自动找到对应的部门去处理。

判断标准只有一条:这种"类型"以后还会不会持续增加?如果会,值得用多态;如果就两三种且永远不会扩展,直接判断就行,不要为了"高大上"而过度设计。

间接 Indirection

你在城里想租房,最直接的方式是找到房东直接谈。但大多数人会通过中介——明明多了个赚差价的人,为什么还要用?因为中介解决了现实问题:你不认识房东,不知道哪套合适,更不想一套套核实虚假信息。中介承担了信息搜集和双方对接的成本,虽然貌似多收了你钱,但实际上降低了整个交易的摩擦。

这就是"间接"的本质:在两个组件之间引入一个中间对象,把直接依赖变成间接依赖,从而降低耦合。

计算机科学界有一句出自图灵奖得主 David Wheeler 的名言:

All problems in computer science can be solved by another level of indirection... except for the problem of too many layers of indirection.

所有计算机科学的问题都可以通过增加一层间接来解决——除了"间接层太多"这个问题本身。一句话道尽了间接这个原则的精髓,以及它的边界。

间接在生活里无处不在

其实你每天都在用"间接",只是没意识到:

  • 领导的助理/秘书:你不能随时直接找老板,他的助理代他过滤沟通,老板只处理真正需要他拍板的事。老板换了,助理换了,对你来说"找老板"这件事的流程没有变。
  • 律师代理:两家公司谈判,双方都派律师,不直接对话,避免一言不合撕破脸。律师是中间人,谁家内部的立场怎么变,对方感知不到细节,只看到律师端出来的方案。
  • 猎头:公司不好意思直接去竞争对手那里挖人,通过猎头走,双方都体面,关系也不僵。
  • 外卖平台:餐厅和顾客不认识,平台在中间撮合,餐厅换地址了、顾客搬家了,对彼此没有影响,只要平台在就行。

这些中间人存在的意义,不是为了"多一道手续",而是让两端可以各自变化,互不干扰。程序里的"间接"也是同样的逻辑,只是换成了技术组件来充当这个中间人的角色。间接,是实现低耦合最直接的手段。

过度间接:中间人多了,事儿就办不成了

间接虽好,滥用起来也是一种折磨。

见过那种报销流程吗?200 块的打车费:先发给直属领导,领导转部门经理,部门经理发财务主管,财务主管还需要 CFO 签字,CFO 出差了转给秘书,秘书说要走 OA 系统,OA 系统填完还要打印出来盖章扫描再上传……两周后,你已经忘了自己打了个车。每一层都是"中间人",每一层都有它存在的"道理",但整体的结果是:一件极小的事,被层层转手整成了极大的麻烦。

中间人要用在真正需要的地方,不是每件事都要套一圈"走流程"。到底哪些地方值得加一道中间环节、哪些地方直来直去更好——这就是下面这个原则要讨论的核心问题了。

纯虚构 Pure Fabrication

每家公司里都有一些不直接产生业务价值的角色:HR、行政、IT 支持、法务。他们不参与任何核心业务,不开发产品,不服务客户,不直接产生营收。但如果你把这些人全裁掉,公司用不了三个月就会一团乱——没人发工资,没人管电脑,没人管合同,没人办入职。

这些角色存在的意义,不是为了创造业务价值,而是为了让整个组织能够顺畅运转。他们是现实业务流程里不存在的角色,是为了组织运作效率而"虚构"出来的支撑岗位。

面向对象设计里,这叫做 纯虚构(Pure Fabrication)

信息专家撑不住的地方

信息专家原则说:谁拥有这方面的信息,谁来负责处理这方面的职责。听起来合理,但有一类职责,在业务领域里找不到合适的现实对象来承担。

举个例子。一家餐厅,厨师是菜品的专家,他对食材、烹饪、口味了如指掌。按照信息专家原则,跟菜有关的事都该厨师来管。但你要让厨师同时负责收款、开发票、做账目、对账、跑税务局报税……他也许做得到,但从此菜就做不好了,根本无法专注在自己的核心职责上,高内聚直接崩掉。

但是,会计这个职位,在一家餐厅的"业务流程"里原本是不存在的——客人来了点菜、上菜、付钱,哪里有"账务处理"这个环节?所以会计是一个被"虚构"出来、专门服务于运营需要的支撑角色。餐厅的业务领域里找不到他的位置,但如果没有他,这家店根本转不起来。

这就是纯虚构:当信息专家原则、高内聚、低耦合三个目标发生冲突时,发明一个在现实业务领域里不存在的角色来平衡它们。 他不生产业务价值,但他让整个系统更干净、更容易维护。

现实中到处都是纯虚构的角色

仔细想想,这类"虚构角色"在现实里比比皆是:

  • 会计事务所:不生产任何产品,但企业账目离不开它。"做账"这件事在原始的商业交换里是不存在的,但规模大了之后,非得有专人专职来做不可。
  • 公证处:买卖双方各有自己的信息,公证处本身不参与任何交易,但它的存在让交易更可信、更安全。
  • 物业公司:住宅的业主才是房子的信息专家,但让每个业主自己管楼道卫生、自己修电梯、自己收水电费,那是一场噩梦。物业这个角色,就是为了让小区能顺畅运转而"虚构"出来的。

这些角色的共同特点:不在核心业务流程里,但承担了让系统正常运转的"幕后职责"。程序设计里,我们也需要类似的角色来处理那些"信息专家们不该亲自干、但又必须有人干"的事情。

公司综合部:纯虚构的反面教材

很多公司都有一个叫"综合部"或"行政部"的部门,最开始设立的时候职责清晰:负责后勤、行政、对外联络。但时间一长,这个部门就变成了整个公司的"垃圾桶"——什么不知道归谁管的事,都扔给综合部。结账找他们,订酒店找他们,出了个法律纠纷找他们,甚至技术部门的电脑坏了也找他们……

这已经不是"纯虚构"了,这是一个内聚度接近于负数的部门。真正合格的纯虚构角色,职责必须单一,边界必须清晰。"什么都管的综合部"不是设计清晰的产物,是懒得思考归属问题的借口。程序里的"万能工具类",跟这个综合部是一个毛病。

预防变化 Protected Variations

需求变更是软件开发里最确定的事情,比线上 Bug 还确定。产品经理在跟你讲需求的时候,他自己也不完全确定最终长什么样。甲方更是如此,往往连自己要什么都说不清楚,只有看到实物才知道"不对"。

但"需求一定会变"不是摆烂的理由,不是"反正要改就随便写"的借口。随便写导致的结果是:每次需求变化都是一场地震,改一行代码引发连环崩溃,最后谁都不敢动,只能不停打补丁,代码越来越丑,人越来越崩溃。

预防变化(Protected Variations) 的核心思想:你阻止不了变化,但你可以控制变化的影响范围。

就像建筑里的防火墙设计,火灾不可避免,但通过防火分区和隔离带,火势扩散的范围是可以被限制的。好的软件设计也是如此——在可预见的变化点周围,建立稳定的接口屏障,让变化只在局部发生。

识别变化点:最考验经验的地方

预防变化的第一步是识别"什么会变"。这比干活难多了,需要对业务和人性都有足够的理解。

大概率会变的东西:

  • 甲方的需求(这不用解释,是职场公理)
  • 合作方(今天用这家供应商,明天嫌贵换一家,接口和协议都不一样)
  • 公司的政策规定(促销规则、积分兑换比例、考核方式——每年都在变的那些)
  • 汇报的领导(领导走了来了,同样一件事上面换了新想法,下面就得跟着动)

相对稳定的东西:

  • 核心业务的骨架(一家餐厅,"点菜—上菜—结账"这个主流程几十年没变过)
  • 人情世故的基本逻辑(尊重、信任、诚信,这些不会因为换了平台就消失)

对会变的地方,留一道缓冲;对稳定的部分,直接实现,不要过度包装。

防弹衣不是每件衣服都要穿

说到这里,要提一个与之对立的原则:你不需要它(YAGNI,You Aren't Gonna Need It)

买保险的人都懂这个道理——你不可能把所有险种全买了,"以防万一"可以无限延伸。你得判断哪些风险大概率会发生,哪些是可以承受的小概率,然后对应地投保。过度买保险等于把钱埋土里,每个月交保费心疼死,最后一次都没用上。

程序设计也是一样。不是每个地方都要"留接口",那等于给每面墙都留插座、给每把椅子都装轮子,看起来考虑周全,实际上是在给自己和接手的人制造麻烦。在该保护的地方保护,在不需要的地方省力气——这才是务实的态度。

一个粗略但实用的判断标准:

这个东西,在这个项目的历史上被迫改过两次以上吗?

如果是,加防弹衣。如果不是,穿件普通衬衫就够了,等到第一次变化真正发生,再决定要不要保护,那时候你对变化点的判断也会准得多。

预防变化是前面多态和间接两个原则在更高维度上的统领:这两者告诉你"怎么做",而预防变化告诉你"在哪里做"——答案由变化点决定,不由技术偏好决定。

创造者 Creator

职场里有一个永恒的问题:这个会,谁来组织?

一般来说不用明文规定,大家也都有默契:项目出了技术问题,技术负责人拉会;需求评审,产品经理组织;部门例会,部门经理召集;公司年会,HR 来安排。背后的逻辑是:谁最了解这件事、谁最需要这件事完成、谁掌握这件事所需的信息,谁就来牵头。

这不是制度规定的,而是一种自然的、合理的组织逻辑。面向对象设计里的 创造者(Creator) 原则说的就是同一件事:对象的创建责任,应该分配给对这个对象最了解、和它关系最近的那个类。

谁最近、谁最懂、谁来创建

这个逻辑在现实里很清楚:孩子出生,户口登在谁名下?当然是父母,因为父母是这个孩子最直接的"包含关系",所有必要的信息都在父母这里。

一个项目的立项文件,谁来起草?当然是项目负责人,不是CEO,不是前台,而是那个最清楚这个项目要做什么、有哪些依赖、需要哪些资源的人。

部门新进来一个人,谁来带他做入职培训、分配工位、介绍业务?当然是部门经理,不是公司前台,也不是CEO——因为部门经理最清楚这个新人来了要做什么事、跟谁合作、要用什么工具。

规律很清晰:谁包含这个新角色、谁记录和使用这个新角色、谁拥有创建这个新角色所需的全部信息——谁就是最合理的"创建者"。 GRASP 只不过是把这个生活常识翻译成了设计原则。

最常见的反面:让大老板管所有人的入职

职场里有一种常见的混乱:公司扩张,每个部门都在招人,但所有新员工的入职手续、第一周的培训、工位分配、业务讲解,全都要通过CEO的秘书来走——因为"流程统一"。结果就是:CEO的秘书要了解每个部门的业务,要知道每个新人需要什么权限、坐哪个工位、找谁对接……她根本不了解这些细节,只能到处问,到处转达,信息在传递中不断失真,新员工第一天什么都搞不清楚。

显然更合理的做法是:统一入职手续归HR管(他们掌握合同、薪酬、社保这些信息),具体的业务对接归各部门经理管(他们掌握岗位、职责、协作关系这些信息)。谁掌握信息,谁负责创建和引导。

设计里也是同理。把本该由"直接包含者"来创建的东西,全部集中到一个统一的服务层去创建,表面看起来"管理统一",实际上是让服务层承担了它根本不应该了解的细节,耦合度悄悄上升,信息专家原则也悄悄被破坏了。

创造者与工厂

有人会问:工厂模式不就是专门管"创建"的吗,为什么还需要 Creator 原则?

打个比方:正常家庭养孩子,父母负责,这是 Creator 原则的基本应用——自然、直接、谁最近谁来。但如果有人孩子太多、管不过来,或者有特殊需求,就会送去寄宿学校、托管机构,这就是"工厂模式"——把"创建和培养"这件事外包给一个专门的机构来做。工厂的出现不是否定父母,而是当"创建"本身变得复杂的时候,才值得专门设立一个角色来负责。

Creator 告诉你正常情况下谁最有资格来负责创建;工厂告诉你当创建过程本身变得复杂之后该怎么设计。两者是递进关系,不是替代关系。

最后说一句

GRASP 九个原则,走完了一遍。

信息专家 说让懂的人来干;创造者 说让关系最近的来生;控制器 说找个专门的人接活、分活,自己别插手;低耦合 说少管别人的事;高内聚 说把自己的事管好;多态 说同类问题让各自的类型去处理,别用 if-else 地摊代码;间接 说两个互不相识的,靠中间人牵线搭桥;纯虚构 说现实里没有的角色,可以为了设计干净而发明;预防变化 说知道哪里会变,就在那里建防火墙。

九条原则,没有一条是高深莫测的。每一条放到职场里、放到日常生活里,都是再朴素不过的道理。

难就难在:当你身处一个复杂的系统,面对几十个对象、几百个接口、几千行代码的时候,还能把这九条原则记在心里,让每一个设计决策都经得起推敲。什么样的项目在设计阶段就注定失败,什么样的架构是在给自己挖坑,什么样的代码改起来举重若轻——这些判断力,不是靠背概念能得到的,靠的是一次次在实践中对照这些原则反思和沉淀。

文章开头说的那句话,送给结尾:

"不要用一个锤子去解决你遇到的所有问题。"

GRASP 给了你九把锤子。用哪把,看情况。

posted @ 2026-08-17 10:10  向空月  阅读(2)  评论(0)    收藏  举报