最近公司发布会在即,同时自己还在适应的过程,要理解公司的业务,要学习新的技术栈,忙乱之中,自己也有一段时间没有好好思考和总结了,ARTS 打开也停滞 3 周了,这个周末都会补上来的,也该总结一下自己这段时间的学习成果,加油吧,这里聊聊自己这段时间迭代代码,修 bug 的一些思考吧,感触还挺深的。
破窗理论
如果我看到一座丢弃的房子里有很多已经破坏的窗口,我不会因为捡起一块石子杂碎窗户而感到内疚。
映射在代码中,如果你发现系统中有很多的烂代码,你就有可能去写更多的烂代码,因为你可能会在心中疯狂暗示自己,这系统反正已经这样了。
其实这是很危险的信号,对系统和个人的成长都是很不利的,因此,首先要做的就是,警惕破窗理论,一定要要求自己,在自己当前最大的能力范围内,不要留下破窗(烂代码)。
内聚与耦合
如果一个模块体积越来越大,你可能就要思考,能不能进行拆分,关于拆分一般有如下思考方向(如果本身功能比较简单,也就没必要进行拆分),因此我们也要将文件大小考虑进去
- 文件大小
- 业务无关工具类
- 业务功能细分类
关于高内聚和松耦合,这句话从大学刚学计算机就一直被灌输,但是感觉自己一直没有很好的理解。向我想起来之前看的一篇博客的思考,最近重新翻来看,感触就更加深刻了。
关于 MVC 和 MVVM 两个模块,最主要的区别不在于模块划分,而是模块间交互,在划分方面它们都致力于将 UI 和逻辑分开,为什么呢,因为 UI is cheap,UI 可能隔三岔五就被设计师推翻再来一套,但是逻辑和数据,相对会稳定一点,所以把他们分开,UI 迭代的时候涉及的改动就比较小。
为何今年前端一直提倡所谓的组件化开发呢,貌似是要把 UI 和逻辑搞在一起,其实并不是,我想就是高内聚的表现吧,因为组件是一个小粒度的模块,它相对独立,具有很高的内聚性,组件的“逻辑”更多的应该是和 UI 相关的交互逻辑,而不是比较底层的逻辑,我们还是应该把相对稳定的逻辑抽出来放到更下一层。
而很多开发人员都意识不到这一点,导致代码会很难看,因此框架的作者们,试图设计一种规范唤起开发者在这一方面的意识,很多时候往往会遭到开发者的批评,为什么要引入这么多的概念。现在我想来 redux 和 vuex,还有 angular 中的 service,除了担任数据状态管理的功能,也起到了逻辑代码下层的作用吧。
高内聚和松耦合:需要进一步理解
基本原则
开闭原则:对扩展开放,对修改关闭。TODO:需要进一步理解
DRY 原则:不要重复自己
SRP 原则:单一职责,单一的函数有更强的复用性,尽量使用纯函数
LKP 原则:最小知识原则,一个对象应该对另一个对象有最少的了解,你内部如何复杂都没关系,我只关心调用的地方
感触
修 bug 的过程中,发现一个很神奇的问题,父组件通过属性传值的方式给子组件传值,子组件内部通过事件的方式更新父组件值,在父组件事件接收函数的 debugger 过程中发现,还没开始赋值,对应的属性就已经有值了,而且还和子组件传过来的相等,导致属性更改的生命周期没有触发。找了好久发现是子组件不小心直接更改了父组件传过来的值,因为如果父组件传递的是引用类型,引用类型的一些变异方法是可以直接修改父组件的值的。
直接就让我想起了当初学 React 时,React 推崇的拥抱数据不可变性。当然 React 拥抱数据不可变性的最大原因还是出于性能考虑,这样 React 检测数据变更时,递归比对,复杂度太高,不可接受,因此 React 妥协,只做浅对比,甚至可以使用 immutable
提高性能。
映射在 angular 中其实也是一样的,当然首先要肯定的一点是,直接在子组件中修改 props 的值的做法无疑是错误的。但如果我们对 props 和 state 遵循数据不可变性,是不是也就避免这个问题了呢。我想是这样的。
这段时间在基于别人的代码重构一个小的功能点,本以为会很简单,但结果就是我差点自闭了,由于不了具体的业务需求,加上代码不知道是有历史债务还是咋地,代码写的确实有点乱,强忍着修改了两次之后,还是决定直接重构了,因为我是真的看不懂。这次小小的重构,也总结了一点心得。大致如下:
如果你没有引入 TypeScript,你可能会看到别人写代码有些函数是下划线打头的,那时候我觉得为什么要这么做啊,这样代码看着好变扭好丑哇。其实这样做的目的就是为了声明该函数仅在该模块内部使用,这样做的目的就是方便日后重构,不用担心外部有使用到它。如果不这么做,就像我本次重构每个函数时,我都要全局搜索一下,是不是还有其他地方使用到它。在 TypeScript 给我们带来了更加优雅的写法,提供了三种作用于修饰符:public、protected、private。
还有一些感触很深的问题如下:
- 过多的全局变量,且变量容易在多个非纯函数中被修改,制造极大的不舒适感,毕竟共享可变状态是万恶之源
- 逻辑代码没有下层,导致组件臃肿,可能也是由于这个原因,大多数函数均非纯函数,均有副作用
- 函数调用混乱,甚至存在在一个 tick 中多次调用同一个函数,对 debugger 特别不友好
最后再来谈谈一个信号的问题:命名很重要,大到业务模块小到辅助函数,只要你觉得不好命名,那就是一个信号,说明这段代码做的事情太多太杂,以至于你无法用几个单词概况出来。