背景
code review 的时候发现有个 getMsgContentByType 函数,有100多行,作用是处理 message 消息,根据message 类型返回对应的内容,代码如下:
const getMsgContentByType = (msgContent: MsgContent) => {
if (msgContent.type === '1') {
// 处理逻辑
return {
title,
subTitle,
image,
//...
};
} else if (msgContent.type === '2') {
// 处理逻辑
return { ...}
} else if (msgContent.type === '3') {
// 处理逻辑
return { ...}
} else {
return {};
}
}
代码分析
代码中的问题:
- 过多的if-else分支: 函数里很多的if else,而且随着需求的增加, if else 越来越多,函数 会变得越来越长和复杂,不利于代码的可读性和可维护性。
- 违反单一职责原则: 函数集中了太多不同类型消息的处理逻辑,职责过重,违反了单一职责原则,这使得代码难以测试和扩展。
- 缺乏扩展性: 当需要添加新的消息类型时,需要修改这个函数,违反了"开闭原则"(对扩展开放,对修改关闭),增加了代码的维护成本。
- 缺乏灵活性: 每个分支的处理逻辑都写在函数内部,这降低了函数的灵活性。如果需要复用某个分支的逻辑,就需要重复代码。
解决方案
1、将不同类型消息的处理逻辑拆分到独立的函数中
在初始代码中,所有的消息处理逻辑都集中在一个函数中。我们将其拆分到独立的函数中, 每个消息都对应自己的逻辑处理函数, 遵循单一职责原则:
// 定义消息处理函数
const handleType1 = (msgContent: MsgContent) => {
// 处理type为'1'的消息逻辑
return {
title,
subTitle,
image,
// ...
};
};
const handleType2 = (msgContent: MsgContent) => {
// 处理type为'2'的消息逻辑
return { /* ... */ };
};
const handleType3 = (msgContent: MsgContent) => {
// 处理type为'3'的消息逻辑
return { /* ... */ };
2、使用映射表管理消息类型和处理函数的关系
接下来,使用一个映射表 messageHandlers 来管理消息类型和处理函数的关系,实现解耦:
// 使用映射表管理消息类型和处理函数的关系
const messageHandlers = {
'1': handleType1,
'2': handleType2,
'3': handleType3,
};
3、 提供一个统一的入口函数
接下来,我们提供一个统一的入口函数 getMsgContentByType,负责根据消息类型调用对应的处理函数:
// 提供一个统一的入口函数
const getMsgContentByType = (msgContent: MsgContent) => {
const handler = messageHandlers[msgContent.type];
return handler ? handler(msgContent) : {}
};
4、定义类型枚举
使用 enum 定义 message 类型,更好的表达消息类型的语义,提高代码的可读性:
// 定义message 类型枚举
enum MessageType {
GIFT = 1,
SUBSCRIBE = 2,
FOLLOW = 3,
}
5、优化后的完成代码
// 定义message 类型枚举
enum MessageType {
GIFT = 1,
SUBSCRIBE = 2,
FOLLOW = 3,
}
// 定义消息处理函数
const handleType1 = (msgContent: MsgContent) => {
// 处理type为'1'的消息逻辑
return {
title,
subTitle,
image,
// ...
};
};
const handleType2 = (msgContent: MsgContent) => {
// 处理type为'2'的消息逻辑
return { /* ... */ };
};
const handleType3 = (msgContent: MsgContent) => {
// 处理type为'3'的消息逻辑
return { /* ... */ };
};
// 使用映射表管理消息类型和处理函数的关系
const messageHandlers = {
[MessageType.GIFT]: handleType1,
[MessageType.SUBSCRIBE]: handleType2,
[MessageType.FOLLOW]: handleType3,
};
// 提供一个统一的入口函数
const getMsgContentByType = (msgContent: MsgContent) => {
const handler = messageHandlers[msgContent.type];
return handler ? handler(msgContent) : {};
};
优化后的好处
- 代码可读性和可维护性更好。新增消息类型只需要添加新的处理函数和映射关系,无需修改入口函数。
- 更好地遵循单一职责原则和开闭原则,提高了代码的扩展性。
- 处理函数可以被复用,提高了代码的灵活性。
- 入口函数更加简洁,聚焦于消息类型的分发,责任更加单一。
应用场景
用到3个或3个以上的 if else、switch case 的时候可以考虑把处理逻辑抽离,用 map 映射去代替。
常见的使用场景,例如:
- 页面有多个 tab,切换不同的 tab 有不同的处理逻辑
- 接口有多个错误码,需要根据不同的错误码给出不同的展示或者提示
- ...
用到的设计模式
策略模式、单一职责原则、开闭原则