← Writing

别再迷恋“复用”了!

今天咱不聊 Vite,不聊 Vue 3,来点儿更扎心,更实在的 —— “复用性” 这三个字。

说实话,这“复用性”就像技术圈的“政治正确”,谁要说不重要,感觉就要被喷成筛子。但今天,我要冒天下之大不韪,喊一句: “兄弟萌!别再一上来就想着 ‘复用’ 了!容易走火入魔!”

这话怎么说?来,先看一段最近看到的评论,看完保准你醍醐灌顶:

The main driver of your motivation is reuse. That is a problematic motivation when it comes to software design and implementation, largely because it requires clairvoyance on the part of the designer/developer (unless it’s already been done several times before, i.e. experience); relevant reuse is mostly discovered, not designed up front. … (后面省略)

这段话,简直句句扎心,字字珠玑啊!翻译成人话就是:

“你做技术,上来就想着 ‘我要复用!我要高效!’ 这动机本身就有点问题。因为 ‘复用’ 这玩意儿,太考验你的 ‘预判’ 能力了!除非你已经是老司机,开过无数次一样的车,否则你咋知道啥时候能 ‘复用’,咋 ‘复用’ 才最香?”

有没有感觉被说中了? 🤣 反正我是狠狠地被打脸了!

“复用性”:理想很丰满,现实很骨感

想想咱们平时写代码,是不是经常会有这种 “宏图伟愿”:

  • “这个组件我要设计得足够通用,以后在 A 项目、B 项目、C 项目都能用!”
  • “这个函数我要写得足够灵活,能处理各种情况,一劳永逸!”
  • “这个模块我要架构得足够牛逼,扩展性拉满,十年不过时!”

想法是好的,出发点也是好的。但现实往往是:

  1. 预判个 der 啊! 你辛辛苦苦设计出来的 “通用组件”,最后发现只在一个项目里用过,其他项目根本用不上,或者用起来各种别扭,为了 “复用” 反而增加了复杂度。
  2. 过度设计一时爽,维护重构火葬场。 为了追求 “灵活”,你的函数参数恨不得有十几个,各种配置项眼花缭乱,半年之后,自己都看不懂自己写的代码了,维护和重构简直是噩梦。
  3. 技术迭代快过火箭,十年后?想太多! 技术日新月异,今天流行的框架,明天可能就变成 “老古董” 了。你架构得再牛逼的模块,也抵挡不住时代的洪流,最终还是要被重写或者淘汰。

先写三遍再说!从 React, Angular, Vue 组件说起

评论里举了个例子,我觉得特别形象: React, Angular, Vue 组件的复用。

So it makes perfect sense to first design a component in React, then in Angular if need be. Once you also have to create one for Vue it may make sense to try to extract one common implementation, one that works equally well in all three cases (though often the result is just equally awkward in all three).

这段话的意思是:

“与其一开始就想着设计一个 ‘三端通用’ 的组件,不如先在 React 里写一遍,然后在 Angular 里再写一遍。等你要在 Vue 里写第三遍的时候,你可能才开始有点感觉,‘嗯,好像有些东西可以抽出来复用一下了’。”

是不是很有道理? 与其闭门造车,对着空气 YY “通用性”,不如先撸起袖子干三遍!

  • 第一遍,摸着石头过河,先把功能跑起来。 别想太多,先用最熟悉的方式,最顺手的框架,把功能实现出来,跑通流程,解决燃眉之急。
  • 第二遍,触类旁通,加深理解。 换个框架,换个思路,重新实现一遍。你会发现,同一个功能,不同的框架有不同的实现方式,不同的思路有不同的优缺点。在这个过程中,你会对技术有更深入的理解,对 “复用性” 有更真实的体悟。
  • 第三遍,水到渠成,发现 “真·复用”。 当你写到第三遍的时候,你会发现,哪些逻辑是通用的,哪些是可以抽离出来的。这时候的 “复用”,才是真正有价值的,才是建立在实践基础之上的,而不是空中楼阁。

“数据树 = 视图树”? React 和 Web Components 的 “甜蜜陷阱”

评论里还提到了对 React 和 Web Components 的一些 “批评”,我觉得也很有意思。

One important criticism to React is: “… that’s like React’s biggest simplification, its biggest flaw—they are basically equating your data tree with your view tree …”

这段话是说,React “数据驱动视图” 这个核心思想,既是它最大的简化,也可能是它最大的缺陷。把数据树和视图树直接等同起来,虽然开发起来很爽,很直观,但也容易让人忽略一些更深层次的问题。

评论把这种 “问题” 也延伸到了 Web Components:

This can apply equally to WCs where what is ostensibly a DOM element takes on application responsibilities while having to somehow interact with the rest of the application in some bespoke manner.

Web Components 的初衷是好的,想通过原生组件化,实现更好的 “复用”。但现实是,Web Components 经常会陷入 “为了组件化而组件化” 的怪圈,原本应该只是 DOM 元素的东西,承担了过多的应用层面的职责,导致组件内部逻辑和外部应用耦合度过高,反而失去了 “复用性”。

这就像啥呢? 就像你想用积木搭建一个 “通用零件”,结果发现这个 “零件” 只能在特定的场景下使用,换个场景就各种不兼容,各种需要 “魔改”,最终反而不如直接用普通的积木更灵活。

六边形架构: “接口为王” 的反腐利器!

评论最后提到了 六边形架构 (Hexagonal Architecture) ,这个架构原则简直是 “复用性” 的反腐利器!

… a sound, maintainable solution should dictate the interfaces it depends on (narrowing API or pattern of usage API), rather than accepting the interfaces already exposed by the dependencies.

这段话的核心思想是: “优秀的系统,应该定义自己需要的接口,而不是被依赖项提供的接口所限制。”

啥意思呢? 就是说,别让你的代码去迁就第三方库,而是让第三方库来适应你的代码!

比如,你要用一个第三方库来实现文件上传功能。如果你直接使用第三方库提供的 API,你的代码就会和这个库紧紧绑定在一起,以后想换个库就麻烦了。

但如果你先定义一个 FileUploadService 接口,你的代码只依赖这个接口,然后你再写一个 ThirdPartyFileUploadService 去实现这个接口,这样一来,你的代码就和第三方库解耦了!以后想换库,只需要换一个 Service 的实现就行了,核心业务逻辑完全不受影响!

这种 “接口为王” 的思想,才是实现真正 “复用性” 的关键!

写在最后:拥抱迭代,告别 “一步到位” 的幻想

说了这么多,其实我想表达的核心观点就一句话:

“别再迷恋 ‘复用’ 了!尤其是在项目初期,与其花大量时间去设计 ‘通用组件’,不如先快速迭代,把功能跑起来,在实践中发现真正的 ‘复用点’!”

记住,软件开发是一个不断迭代,不断进化的过程。没有 “一步到位” 的完美方案,只有 “不断演进” 的最佳实践。

拥抱变化,拥抱迭代,先写三遍再说!你会发现,真正的 “复用性”,往往会在不经意间,悄然降临。希望这篇文章能给你带来一些启发和思考。