# 由解耦想到的一些事情

> 原文：https://pjhubs.com/2020/03/18/talk03/
>
> 作者：PJHubs · 发布于 2020-03-18 · 标签：iOS、架构

> 从我大一开始「高内聚，低耦合」这句话就一直在耳边唠叨，但自己对这句话的理解仅仅达到了「要内聚，少耦合」的层面上，到底要多高才叫高内聚，多低才是低耦合，是一点概念没有。

## 经验
> 耦合性(Coupling)，也叫耦合度，是对模块间关联程度的度量。 耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。

这短短的几十字把「解耦」本身说的十分轻巧，但其中所包含的内容却足以支撑起软件工程的整座大厦。如果这个问题从另外一个方面来看的话，微内核与宏内核之争已是上个世纪的战争，一些背景也就不做展开了。

新手也许会跟我一样，初看到「耦合」定会纳闷，怎么去定义一个模块是否耦合？怎么去计算模块或代码中的耦合度是多少？到达了什么样的层面需要去挥起解耦的大刀？这些问题曾经也都在困扰着我。

在去年组内的一个技术分享的 QA 环节上，我提出了一个同等的问题“如何确定此时是否需要开展重构？”，本以为我会得到一个满意的答案，但实际上短短的两个字“经验”的答复，瞬间把我打回了懵懵懂懂的小白。

呵，“经验”！私以为“经验”二字是最不能解决问题的答案，但如今却活生生的流进了耳朵里。因为在我的脑海中，计算机领域目前还没有无法通过具体的实例、代码或公式去描述出的问题，因为这些问题都是通过人本身去实现的，既然是通过人本身主动去完成的。

然而只经过了短短半年的经历，我对“经验”本身产生了巨大的转变，因为有些问题真的只能通过经验经验去解决，甚至在对待重构和解耦这类型更高曾经的工程技术需求时，经验往往是攻坚阶段的最佳利器。

## V 项目
在 V 项目中，业务需求都已经完善好了，但我个人为了一些 UI 洁癖，硬生生的把完成保存至相册所使用的系统 Alert 弹窗去掉，转而使用项目内的 Alert（下文简称 `VAlert`），原本以为就是换个 API 的事情，却没想到前后居然返工了不下六七次。

首先，`VAlert` API 的调用上十分简洁，如下所示：

```objc
[self showMessagerAlertWithMsg:@"PJHubs"];
[self showMessagerAlertWithSucceedMsg:@"PJHubs"];

// ...
```

这个时候问题来了，提测后隔天发现在刘海屏上的展示被遮挡。然后仔细深入去看实现，发现了一个大问题，`VAlert` 居然是要去检测在 `NavigationBar` 的高度做的展示，也就是跟 `NavigationBar` 做了一个强依赖！

但在展示图片的时候只是一个全屏的 `UIView`，当前并没有 `NavigationBar`，看到这里解决思路也有了，总的来说就是做布局的适配，但在布局的适配过程中又发现了布局描述居然使用的是 `AutoLayout` 的 `Visual-Formatter` 格式。

（ 过去了一个小时

学习了一遍 `Visual-Formatter` 怎么写布局，是真的难用，而且一旦写漏一个符号，没有警告或报错，因为这种「可视化」描述语言传入的布局约束是个字符串......

最好总算解决了问题，回过头去看本质，首先是 `VAlert` 提供的 API 不够规范，而直接对 `UIVIewController` 做了拓展，导致其它需要全屏显示视图展示并不能获取的 `NavigationBar`。

想要比较好的解决这个问题，我认为 API 也确实没啥错误，这里的耦合确实足够低，只需要通过一行代码的方法即可完成调用，但具体的实现上出现了问题，也就是「内聚」没考虑好一些边界情况，这个时候作为 RD，从 RD 的角度出发之前我一直是认为封装好的方法，或者已经经过线上几个版本迭代的方法都是扎实可靠的，但实际上这里没有考虑屏幕的适配问题。

同时也给了一些反思，已经跑好的代码/方法就一定是扎实可靠的吗？

## S 项目
S 项目昨天已上线 app store，终于把这个两年半年前的项目重新跑起来推上线了。在重新跑起来的过程中，发现底层引擎的一些问题。S 是一个游戏项目，所运用的是两年半前的 Cocos2dx 版本。

首先说明 Cocos2dx 本身有很多历史包袱问题，问题多到我已经不想再去碰它了。现在越发的觉得如果在一个技术推向市场时，如果不能让开发者快速的构建起配套的工程，流失率会非常大。

如果不是因为 Cocos Creator 和 Cocos2dx 从语言层面上做了完全的隔离，实在是不想再去碰这个烂摊子。第一是 Cocos2dx 本身的问题非常多，每次 Cocos2dx 官方的升级都无法提供一个便捷的方式给开发者进行操作，更何况这个引擎本身就有不少小问题，很多开发者包括我自己都做了很多魔改的操作以此来规避掉这个引擎本身的问题。

第二，在已经对底层引擎做了一部分修改的前提下想要升级这个引擎，几乎不可能，现在社区里还有不少复制粘贴新版本引擎的代码到项目里进行的替换，想想就知道这种方式的效率有多低。

第三，为什么 Cocos 要有 Cocos2dx、Cocos2dx-js、Cocos Studio 和 Cocos Creator 这么多的变种啊！！！

最后好不容易下定决心把 S 这个上古代码迁移到 Cocos2dx 4.0，刚 `pod install` 完编译下工程，直接报 `xcconfig` 没找到，最要命的是，调整了半个多小时的项目目录结构始终无法按照 Xcode 的思路去来，根本不知道 `cocos new` 生成的模版文件里到底做了什么骚操作。

原本还打算继续持续更新维护这个项目，但看到 4.0 了都还不能一键搞定这些原本就已经存在的问题，彻底放弃 cocos2dx 转向 Cocos Creator。

经过以上的描述，大家也能看出这种看上去本应是业务无关的事情，但实际上在开发者这一块却一晚上都没能跑起来，项目目录还是一团糟，Cocos2dx 凭借自己深厚的功力实力劝退。再说一句，虽然现在 cocos2dx 外挂着的游戏宣传，几乎一水儿的把引擎统统改了一番......

嗯，所以就是内聚没弄好，耦合也挺重，业务影响不小。

## 总结
本来只是一个解耦的需求，却让我在做解耦的这段时间里同等联动的思考了不少问题，产品设计很重要，但做一个对后续产品需求有预见性的设计更重要。
