问题背景
业务中,经常需要处理很多相似的数据,这些数据的逻辑有的一样,有的不一样。
比如,聊天消息的数据:
[
{kind: "文字", text: "..."},
{kind: "图片", url: "..."},
{kind: "图文", url: "...", text: "..."},
{kind: "语音", url: "..."},
{kind: "文件", url: "..."},
]
最简单粗暴的做法就是对每种数据写一个处理函数,但又发现某些数据类型的处理逻辑是一样的,如果分开写又会出现大量重复代码。
比如示例中,带 url 的数据都需要下载。
解决思路一
将处理逻辑封装成一个哈希表配置,查表解析,比如:
{
"文字": data => { },
"图片": data => { },
"图文": data => { },
"语音": data => { },
"文件": data => { }
}
之后,根据数据的类型,从配置中提取针对它的处理函数,驱动流程执行。
数据通用的处理逻辑则放在配置外统一实现。
但这个思路的问题是只能提取统一的逻辑,并不能将某些相似的逻辑提取出来。
解决思路二
基于标记的统一配置,将所有数据按特性打上不同的标记,然后有相同标记的数据统一处理,比如:
if (hasText(data)){
// ... 这里处理文字和图文
}
if (hasUrl(data)){
// ... 这里处理图片、图文、语音、文件
}
这是思路一的改良版,支持批量处理相同特性的数据,规避了重复代码。
不过这个思路有两个隐藏的缺点:
1. 一个数据的处理流程被强行拆到了不同地方,很难清晰看出来完整的处理流。如果已知某类数据的处理结果有 BUG,很难定位是在哪个环节出错的。
2. 当需求变更时,原有的标记可能要调整,但调整标记后影响整个处理流程,对代码不熟的后来者大概率会选择跳过标记,直接根据类型作一些特殊处理,导致代码比较混乱。
使用标记的配置方式,看上去非常绝妙,但只有作者会当成非常优雅的设计,却会在接手者中成为难以维护的古老代码。
解决思路三
利用面向对象的多态,为不同的子类实现不同的函数,这个方案的问题是:无法解决某些数据共用一套业务逻辑的场景,因为类是单继承的。
解决思路四
我认为解决这类问题,最优雅的方式还是回归原始,强制分开处理不同的数据:
match (kind) {
case "文字":
// 处理文字
case "图片":
// 处理图片
case "图文":
// 处理图文
case "语音":
// 处理语音
case "文件":
// 处理文件
}
如果每个类型的处理逻辑比较复杂,可以拆分成不同的处理函数。
那将回到原点了,怎么避免重复的处理代码?
方案很简单:需要复用的逻辑提取成公共函数:
match (kind) { case "文字": processText(data) case "图片": processUrl(data) case "图文": processText(data) processUrl(data) case "语音": processUrl(data) case "文件": processUrl(data) }
这样,每个数据的处理流程非常清晰,且可以随意修改,不影响其他数据类型。
如果新增一种数据类型,既可以复用已有的业务逻辑,又可以单独写。
全程没有黑魔法,也没有面向对象中复杂的概念,也不需要记忆各自配置、通用规则。
我认为这是首先应该采用的,最朴素却又最能解决问题的方案。
浙公网安备 33010602011771号