为什么学习和使用设计模式?我认为有两个主要原因:
- 解耦:设计模式的目标是将 "不变的" 和 "可变的" 分离,将 "不变的" 封装为统一对象,而在具体实例中实现 "可变的" 部分。
- 统一标准:设计模式定义了一套优秀代码的标准,相当于一份实现优秀代码的说明书。
在前端开发中,面对复杂场景,我们可以通过设计模式更清晰地处理代码逻辑。其中,策略模式在前端开发中的应用非常广泛。下面我将详细介绍策略模式在前端开发中的具体应用。
策略模式基础策略模式的含义是:定义了一系列的算法,并将每个算法封装起来,使它们可以互相替换。
个人对策略模式的理解是:将原本写在一个函数中的一整套功能拆分为独立的部分,以达到解耦的目的。因此,策略模式最适用于拆解 if-else 结构,将每个 if 模块封装为独立的算法。
在面向对象的语言中,策略模式通常由三个部分组成:
- 策略(Strategy):实现不同算法的接口。
- 具体策略(Concrete Strategy):实现策略定义的接口,提供具体的算法实现。
- 上下文(Context):持有策略对象的引用,通过一个具体策略对象进行配置,并维护对策略对象的引用。
可能这样的定义不太直观,因此我使用 TypeScript 和面向对象的方式来实现一个计算器的策略模式示例,以便更好地说明。
typescript:
// 第一步:定义策略(Strategy)
interface CalculatorStrategy {
calculate(a: number, b: number): number;
}
// 第二步:定义具体策略(Concrete Strategy)
class AddStrategy implements CalculatorStrategy {
calculate(a: number, b: number): number {
return a + b;
}
}
class SubtractStrategy implements CalculatorStrategy {
calculate(a: number, b: number): number {
return a - b;
}
}
class MultiplyStrategy implements CalculatorStrategy {
calculate(a: number, b: number): number {
return a * b;
}
}
// 第三步:创建上下文(Context),用于调用不同的策略
class CalculatorContext {
private strategy: CalculatorStrategy;
constructor(strategy: CalculatorStrategy) {
this.strategy = strategy;
}
setStrategy(strategy: CalculatorStrategy) {
this.strategy = strategy;
}
calculate(a: number, b: number): number {
return this.strategy.calculate(a, b);
}
}
// 使用策略模式进行计算
const addStrategy = new AddStrategy();
const subtractStrategy = new SubtractStrategy();
const multiplyStrategy = new MultiplyStrategy();
const calculator = new CalculatorContext(addStrategy);
console.log(calculator.calculate(5, 3)); // 输出 8
calculator.setStrategy(subtractStrategy);
console.log(calculator.calculate(5, 3)); // 输出 2
calculator.setStrategy(multiplyStrategy);
console.log(calculator.calculate(5, 3)); // 输出 15
前端策略模式应用:
实际上,在前端开发中通常不会直接使用面向对象的模式。在前端中,策略模式可以简化为两个部分:
1. 对象:存储策略算法,并通过键(key)匹配对应的算法。
2. 策略方法:实现键对应的具体策略算法。
我将举一个最近在开发中应用策略模式进行重构的例子。该例子实现了对不同操作的联动字段处理。在原始代码中,针对操作类型 opType 大量使用了 if-else 判断。虽然看起来代码量不多,但每个 if-else 块中可能包含大量处理逻辑,导致整体的可读性变差。
typescript:
export function transferAction() {
actions.forEach((action) => {
const { opType } = action;
// 展示/隐藏字段
if (opType === OP_TYPE_KV.SHOW) { }
else if (opType === OP_TYPE_KV.HIDE) { }
// 启用/禁用字段
else if (opType === OP_TYPE_KV.ENABLE) { }
else if (opType === OP_TYPE_KV.DISABLE) { }
// 必填/非必填字段
else if (opType === OP_TYPE_KV.REQUIRED) { }
else if (opType === OP_TYPE_KV.UN_REQUIRED) { }
// 清空字段值
else if (opType === OP_TYPE_KV.CLEAR && isSatisfy) { }
});
}
经过策略模式重构之后,我们将每个操作封装为单独的方法,并将所有算法放入一个对象中,通过触发条件进行匹配。重构后的代码相较于原始的 if-else 结构更加清晰,每次只需找到对应的策略方法实现即可。此外,如果需要扩展功能,只需继续增加策略方法,而不会影响原有代码。
typescript:
export function transferAction(/* 参数 */) {
/**
* @description 处理字段显示和隐藏
*/
const handleShowAndHide = ({ opType, relativeGroupCode, relativeCode }) => {};
/**
* @description 启用/禁用字段(支持表格行字段的联动)
*/
const handleEnableAndDisable = ({ opType, relativeGroupCode, relativeCode }) => {};
/**
* @description 必填/非必填字段(支持表格行字段联动)
const handleRequiredAndUnrequired = ({ opType, relativeGroupCode, relativeCode }) => {};
/**
@description 清空字段值
*/
const handleClear = ({ opType, relativeGroupCode, relativeCode }) => {};
// 联动策略
const strategyMap = {
// 显示/隐藏
[OP_TYPE_KV.SHOW]: handleShowAndHide,
[OP_TYPE_KV.HIDE]: handleShowAndHide,
// 启用/禁用
[OP_TYPE_KV.ENABLE]: handleEnableAndDisable,
[OP_TYPE_KV.DISABLE]: handleEnableAndDisable,
// 必填/非必填
[OP_TYPE_KV.REQUIRED]: handleRequiredAndUnrequired,
[OP_TYPE_KV.UN_REQUIRED]: handleRequiredAndUnrequired,
// 清空字段值
[OP_TYPE_KV.CLEAR]: handleClear,
};
// 遍历执行联动策略
actions.forEach((action) => {
const { opType, relativeGroupCode, relativeCode, value } = action;
if (strategyMap[opType]) {
strategyMap[opType]({ /* 入参 */ });
}
});
}
总结:
策略模式的优点在于,代码逻辑更清晰,每个策略对应一个实现方法,并且遵循开闭原则。新的策略方法无需改变已有代码,因此非常适合处理或重构复杂逻辑中的 if-else 结构。
在前端开发过程中,并不需要完全遵循面向对象的应用方式。我们只需通过对象存储策略算法,并通过键进行匹配,即可实现基础的策略模式。
通过学习和应用设计模式,我们能够提高代码的可维护性和扩展性,使代码更加灵活和清晰。策略模式是前端开发中常用的设计模式之一,它能帮助我们处理复杂的代码逻辑,提升开发效率。
通过深入理解和灵活运用设计模式,我们可以成为精通前端设计模式的开发者,从而编写出高质量的代码并更好地应对复杂的开发场景。
感谢阅读本文,如果对你有帮助,请点赞和收藏
标签:精通,opType,策略,number,KV,设计模式,TYPE,耦中,OP From: https://blog.51cto.com/u_7669561/6781321