提升前端流程可扩展性的设计模式应用:责任链与工厂模式

在开发涉及多步骤的前端流程时,传统的“硬编码”方式会导致代码耦合度高、流程不易扩展、维护成本大。当我们希望在流程中增加或修改某个步骤时,常常需要在多处改动代码,这不利于项目的长期维护。为了克服这些问题,我考虑使用责任链模式工厂模式来优化这类需求。责任链模式能够将每个步骤解耦,使每个步骤独立处理,互不干扰,而工厂模式可以集中管理各个步骤的创建逻辑,确保整个流程的灵活性和可扩展性。通过结合这两种模式,我们可以构建一个清晰、易维护的代码结构,从而更高效地应对未来的变化和扩展需求。

示例案例:支付流程中的步骤实现

假设你正在开发一个简单的电商支付流程,前端需要逐步实现从选择商品输入卡号再到确认付款的过程。为了让这些步骤之间能够顺畅地流转,我们要避免传统的“硬编码”方式,转而采用更灵活的设计模式。那怎么做呢?我们这次用上了 责任链模式 和 ​工厂模式​,来实现一个既模块化又好维护的多步骤界面。

1. 使用责任链模式来管理步骤

责任链模式可以理解为“接力棒”式的操作,每个步骤都独立处理,完成后将请求交给下一个步骤。在我们的案例中,每个步骤都是一个处理器,按顺序完成当前任务后,再转给下一个。这种设计特别适合这种线性流程,比如“选择商品 -> 输入卡号 -> 确认付款”这种步骤间有明确顺序的任务。

2. 使用工厂模式来创建处理器

工厂模式可以帮助我们集中管理处理器的创建过程。简单理解就是,我们用工厂模式来动态生成每个步骤的“处理器”,比如“选择商品处理器”、“输入卡号处理器”等。这样一来,未来如果要增加或者修改某个步骤,只需要调整相应的处理器,而不必动其他部分的代码。

责任链和工厂模式的优势是什么?

  • 解耦每个步骤的逻辑​:每个步骤各自独立,只需完成自己的任务后交给下一个处理器,不需要相互干扰。这样当我们需要修改某一步时,不必担心影响到其他步骤的实现。
  • 便于扩展和修改​:未来如果我们想加入一个 选择优惠券 的步骤,只需新增一个处理器,把它插入到链条中即可,不需要改动现有的步骤逻辑。
  • 清晰的流程控制​:责任链模式让每个步骤执行得非常明确,从选择商品开始,逐步进入下一个环节,直到最终完成付款。整个流程清晰易懂,省去了复杂的条件判断。
  • 提高代码可测试性​:因为每个步骤都是独立的处理器,我们可以单独对每个步骤进行测试,这样测试起来更简单,也更有针对性。

示例:商品支付流程代码实现

HTML 结构

首先,我们为每个步骤设置了不同的 HTML 元素,每个步骤都有一个“下一步”按钮,来触发进入下一个步骤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="container">
<div id="step1" class="step">
<h2>选择商品</h2>
<select id="product">
<option value="product1">商品 1</option>
<option value="product2">商品 2</option>
<option value="product3">商品 3</option>
</select>
<button class="next-button">下一步</button>
</div>

<div id="step2" class="step">
<h2>输入卡号</h2>
<input type="text" id="cardNumber" />
<button class="next-button">下一步</button>
</div>

<div id="step3" class="step">
<h2>确认付款</h2>
<button id="confirmButton">确认付款</button>
</div>
</div>

CSS 动画

为了提升用户体验,我们使用了 CSS 的渐显动画,让每个步骤显示得更加自然流畅。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.step {
display: none;
}

.step.active {
display: block;
animation: fadeIn 1s;
}

@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

JavaScript 逻辑

接下来,我们使用责任链模式和工厂模式来实现 JavaScript 逻辑,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class StepHandler {
constructor(name, nextHandler = null) {
this.name = name;
this.nextHandler = nextHandler;
}

setNext(handler) {
this.nextHandler = handler;
}

handle() {
if (this.nextHandler) {
this.nextHandler.handle();
}
}
}

class Step1Handler extends StepHandler {
handle() {
document.getElementById("step1").classList.remove("active");
document.getElementById("step2").classList.add("active");
super.handle();
}
}

class Step2Handler extends StepHandler {
handle() {
if (document.getElementById("cardNumber")) {
document.getElementById("step2").classList.remove("active");
document.getElementById("step3").classList.add("active");
super.handle();
}
}
}

class Step3Handler extends StepHandler {
handle() {
alert("付款成功!");
}
}

class StepHandlerFactory {
static createStepChain() {
const step1 = new Step1Handler("选择商品");
const step2 = new Step2Handler("输入卡号");
const step3 = new Step3Handler("确认付款");

step1.setNext(step2);
step2.setNext(step3);

return step1;
}
}

document.addEventListener("DOMContentLoaded", function () {
const stepChain = StepHandlerFactory.createStepChain();

// 初始化第一步
document.getElementById("step1").classList.add("active");

document.querySelectorAll(".next-button").forEach((button) => {
button.addEventListener("click", () => {
stepChain.handle(); // 执行责任链的处理
});
});

document.getElementById("confirmButton").addEventListener("click", () => {
alert("付款成功!");
});
});

小结

通过将责任链模式和工厂模式结合,我们为前端支付流程提供了更加清晰和灵活的实现方式。责任链模式帮助我们解耦每个步骤的处理逻辑,使得各步骤之间互不干扰,而工厂模式则集中管理每个步骤的创建,便于以后扩展和维护。这样的设计让我们的代码更有条理、更具可扩展性,并且测试起来也更加方便。

如果你有任何疑问或者更好的实现建议,欢迎在评论中与我们讨论!

增强版本

可以通过类型来指定只执行符合类型的步骤,那么这里会使用到 TS,并且会通过类型判断来执行对应的步骤。这里只是一个简陋的代码实现,如果你有更好的实现方案,请在评论留言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// 步骤的类型
type StepType = "product" | "money";

// 定义商品类型
interface Product {
name: string;
price: number;
}

// 定义金钱类型
interface Money {
amount: number;
cardNumber: string;
}

// 步骤处理器类
class StepHandler<T> {
constructor(
public type: StepType,
public nextHandler: StepHandler<T> | null = null
) {}

// 处理当前步骤
handle(input: T): void {
// 当前步骤处理完后,决定是否执行下一个步骤
if (this.shouldHandle(input)) {
console.log(`处理步骤:${this.type}`);
// 如果当前步骤处理了,并且有下一个步骤,则交给下一个步骤
if (this.nextHandler) {
this.nextHandler.handle(input);
}
} else {
// 如果当前步骤不适用,直接跳过,传递给下一个步骤
if (this.nextHandler) {
this.nextHandler.handle(input);
}
}
}

// 判断是否处理该类型的步骤
private shouldHandle(input: T): boolean {
// 根据当前步骤类型来判断
if (this.type === "product" && this.isProduct(input)) {
return true;
} else if (this.type === "money" && this.isMoney(input)) {
return true;
}
return false;
}

// 类型保护 - 检查是否为 Product 类型
private isProduct(input: T): input is Product {
return (input as Product).price !== undefined;
}

// 类型保护 - 检查是否为 Money 类型
private isMoney(input: T): input is Money {
return (
(input as Money).amount !== undefined &&
(input as Money).cardNumber !== undefined
);
}
}

// 工厂方法:根据步骤类型创建链条
class StepHandlerFactory {
// 创建Product相关步骤链
static createProductStepChain(): StepHandler<Product> {
const step1 = new StepHandler<Product>("product");
const step2 = new StepHandler<Product>("product");
const step3 = new StepHandler<Product>("product");
step1.nextHandler = step2;
step2.nextHandler = step3;
return step1;
}

// 创建Money相关步骤链
static createMoneyStepChain(): StepHandler<Money> {
const step4 = new StepHandler<Money>("money");
const step5 = new StepHandler<Money>("money");
step4.nextHandler = step5;
return step4;
}

// 创建联合类型(Product | Money)的责任链
// 这个方法根据传入的类型(Product 或 Money)来创建对应的链条
static createCombinedStepChain<T extends Product | Money>(
type: StepType
): StepHandler<T> {
if (type === "product") {
// 创建商品链条
return StepHandlerFactory.createProductStepChain() as StepHandler<T>;
} else if (type === "money") {
// 创建金钱链条
return StepHandlerFactory.createMoneyStepChain() as StepHandler<T>;
}
throw new Error("未知的步骤类型");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用商品类型执行流程
const productChain = StepHandlerFactory.createCombinedStepChain("product");
const productInput: Product = { name: "商品1", price: 100 };
productChain.handle(productInput); // 只会处理商品相关的步骤

// 使用金钱类型执行流程
const moneyChain = StepHandlerFactory.createCombinedStepChain("money");
const moneyInput: Money = { amount: 500, cardNumber: "1234-5678-9876-5432" };
moneyChain.handle(moneyInput); // 只会处理金钱相关的步骤

// 如果需要同时执行商品和金钱的步骤,可以先执行一个链条,再执行另一个链条
console.log("执行商品步骤:");
productChain.handle(productInput); // 执行商品步骤

console.log("执行金钱步骤:");
moneyChain.handle(moneyInput); // 执行金钱步骤