周末两天过的很快,袁帅终于没有把自己锁在书房里,篮球、羽毛球、游泳、踏青、电影,他“报复性”地干了一堆,简单设计也暂时被他抛诸脑后。清扬则不一样,虽然上周五跟袁帅探讨了六种常见的重复,但还有一个疑惑一直在心里,只等见袁帅再论。

    周一一大早,清扬的简单设计工具箱又多了一张卡片,她把三张卡片罗列在桌面上,看着他们所有所思,自从她在Code Review运用简单设计框架来找茬后,小组成员“吵架”貌似也变得更有秩序了。
    简单设计之消除重复 - 图1

    不一会儿,她心中的疑虑又在呼唤她,她想起了王大师的那段评论中举得例子:

    千万不能让它凌驾于“SRP 单一责任原则”之上,如果两者冲突,我宁愿保留一定的重复。 假设有两个计算成本的需求,一个是为了向终端用户证明“你看我们的价格多透明,利润刚刚好”,另一个是为了让运营同事进行成本分析,看到真实的成本。 在系统初建的阶段,两者的算法可能连任何一个细节都是一样的。但是几乎可以肯定的是,它们将来必然分道扬镳,如果让BA来看,现在的相同只会是一个阶段性的偶然状态。 那么我们就有了两个选择:消除重复或者不消除重复。如果消除重复,则违反了 SRP —— 这段代码不应该有两个变化的来源,不应该对两个核心干系人负责。 如果在这里强行消除重复,将来维护的同事只要一不小心就可能会导致严重的线上事故。所以,重复并不必然是万恶之源,不违反 SRP 前提下的重复才是。

    周末浪过火了,袁帅周一请了半天假,到办公室时已经是12:55了。清扬靠在座椅上在闭目思考,感觉到袁帅从身边经过,她立马坐直身子,示意袁帅去会议室。袁帅心里早有准备,放好电脑拿起水杯跟随清扬去了。

    “你怎么看待那个例子?”袁帅很直接地提了个问题。

    “既然是两种不太相关的业务场景,我觉得确实要分开,即便代码存在重复了。如果不分开,SRP会说:‘嘿,兄弟,你这有两个业务方向会引起它变化了。’” 清扬小心翼翼的回答。

    “那个…这样子,咱们先抛开SRP,聊一聊这样的设计存在什么问题呗~”袁帅引导清扬来思考问题。此时他隐约从清扬身上看到了自己最初学习OOD的时候的那种“拿药找病”的激动,那时的他有点过于喜欢拿设计模式和设计原则去论代码设计。

    简单设计之消除重复 - 图2

    袁帅起身拿起白板笔在墙上画了几个示意草图,问道:“分开的话就像现在这个样子,对不?” “对的!”清扬快速接话。

    “在两个场景下都有计算成本的逻辑,按照你的意思是,两边各放一份相同的代码咯!” 袁帅稍作停顿后问了个问题:“运营人员在做查看成本分析报告时发现计算成本的逻辑要改,怎么办呢?”

    “改呗,把右边的计算成本逻辑改掉呗,如果利润透明报告所在业务模块的计算成本也要改的话就再改一次。”清扬小有成就地回答道。

    “等等,闻一闻~”袁帅故作细闻深巷酒香的姿势:“闻到什么了吗?” “去你的~” 清扬伸脚准备踢袁帅,“「霰弹式修改」!”她紧接着不屑地回答。

    “你看啊,我是这么思考这个问题的,如果你在一个业务模块下改计算成本逻辑,另一个模块也跟着要做相同的改动,这是一个典型的重复代码引起坏味道,已经出现问题了,对于问题你总不能做视不管吧。”袁帅耐心讲起来。

    “是的,我觉得应该提取出来,让两个模块共用。可是我很确定未来就有两个不同的业务方向导致它会发生更改啊!”清扬还没讲完,袁帅迅速画出了一个新的交互图:
    简单设计之消除重复 - 图3

    “说得好,两个不同的业务方向引发一个地方发生变化,确实有散发「发散式修改」坏味道的风险。不过你仔细想一想,现在的两个业务方向引起同一个地方发生相同的变化,你觉得这个「相同的变化」正常吗?为什么引发相同的修改呢?是不是这个相同的「计算成本」逻辑缺乏某些抽象呢?”袁帅一连问了几个问题,语气显得一丝丝激动,两眼透着光。

    见清扬被问蒙了,袁帅喝了口水,缓和了一下语气,说到:“你看这样子怎么样,如果现在两个业务模块总是引发相同的修改,维护起来已经别扭了,我们先提取计算成本的逻辑,如果后续业务发展到两个方向引发的改变不相同了…”

    “那到时候再把计算成本的逻辑分开到两个业务模块中去,那时候要分也是因为业务需求发展需要拆分了,这样就避免了前期「霰弹式修改」的味道!”清扬兴奋地抢答,并把手指指向袁帅刚画的图。↑↑↑

    “呀,会抢了啊, 把我想说的话给抢了。”袁帅便开玩笑边对清扬竖起拇指,继续说道:“两个不同的业务模块,存在两段相同的逻辑,未来双方发生变化时会引起相同的逻辑的相同改变,这个时候你要当心了 — 两个业务模块中耦合了同一个隐含的概念,而这个概念很可能就是一种抽象的缺失。

    见清扬挠头疑惑起来,袁帅也不清楚自己刚才说的是人话还是鬼话,便补充了一句人话:“额,不管是SRP还是什么设计模式,我个人是提倡:始终从问题出发,保持对问题的敏感,出现问题解决问题。比如咱们刚才整个过程就是一些代码坏味道驱使着要做什么改动。” 说完他感觉也只有后半句是容易听得懂的人话,于是面色羞愧地低下了头。

    沉默了一会儿,袁帅见清扬在凝视手中的一张手写了SRP描述的红色卡片发呆,他轻轻抽过来拿起笔补充了两句还画了线:
    简单设计之消除重复 - 图4

    袁帅本来没打算这个时候跟清扬聊设计原则,见她这么执着,就加了两句话,然后打开会议室的门朝工位走去。在这短短的30米的路途中,袁帅想起了之前CTO出品的业务建模里面提到的「业务系统」和「领域系统」两个概念,成本计算就可以放在一个「领域系统」里,其他业务系统可以依赖这个领域系统获取成本。

    毕竟是一个缺乏细节描述的列子,袁帅没有过多去针对这个举例深究了。他今天很开心,作为清扬的Buddy,他一直在尝试启发清扬从业务视角去思考、尝试去发现和定义问题。当然,好学的清扬也一直在发生肉眼可见的变化,这也无形中督促他自己不断去学习,即便这么努力,他深知至今自己都未思考透很多优良模式和原则背后的本质。

    “谢谢帅哥!”清扬声音从后面传来,袁帅会心一笑,坐下来打开了IDEA。趁IDEA启动之际,他通过微信给清扬分享了一篇自己之前虚构的一个 SOLID创业故事

    简单设计之消除重复 - 图5