Redux VS 命令模式

Posted by Damon on 2016-05-18

原文 Redux and The Command Pattern

软件行业两大必然事件:

框架永远在变
设计模式是软件工程的基础
想要最佳实践,大多都会归结到设计模式上来。设计模式是由经验丰富的开发者总结的,它教会我们如何思考。但它不一定是最终的解决方案,如果你有更好的方案,完全可以无视设计模式。

有关设计模式推荐两本书 《设计模式:可复用面向对象软件的基础》 & js相关的《Learning JavaScript Design Patterns》
命令模式

命令模式在构建简洁解藕的系统框架是非常出色的。它能让系统在未来某个时刻执行某个部分业务逻辑。Redux 就是来源于此,让我们了解一下命令模式并把它转换为Redux

三要素 Receiver, Command,以及 Executor

例子:用代码描述特斯拉新车Model3的销售方案

Receiver 命令接收器

保存我们的业务逻辑,每当接受到一个需求它能知道如何去满足该需求。

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
/**
+ 确定需求
+ @param model - model of car
+ @param id - id of car
**/

function requestInfo(model, id) {
return `${model} with id: ${id}`;
}

/**
+ 买车
+ @param model - model of car
+ @param id - id of car
**/

function buyVehicle(model, id) {
return `You purchased ${model} with id: ${id}`;
}

/**
+ 安排看车
+ @param model - model of car
+ @param id - id of car
**/

function arrangeViewing(model, id) {
return `You have successfully booked a viewing of ${model} (${id})`;
}

我们将上述的三个操作封装到一个控制器里面:

1
2
3
4
5
6
const TeslaSalesControl = {
buyVehicle,
requestInfo,
arrangeViewing
}
export default TeslaSalesControl;

Command 命令

包含行为调用的一些信息 通常是一个对象

1
2
3
4
const sampleCommand = {
action: "arrangeViewing",
params: ['Tesla 3', '1337']
};

命令定义了一个行为,执行arrangeViewing的行为。

The Executor 执行器

有了命令 也有了命令接收器(控制器),接下来就是连接他们俩的执行器了。执行器的职责就是将命令传给命令接收器来并调用接收器里面的响应函数来完成业务逻辑。

1
2
3
4
5
6
7
/**
+ A generic execute function
+ Takes a receiver and a command
**/

export default function execute(receiver, command) {
return receiver[command.action] && receiver[command.action](...command.params);
}

现在我们就可以开始执行这些命令了。

让业务跑起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import execute from 'executor';
import TeslaSalesContorl from 'receiver'

// 确认需求
execute(TeslaSalesContorl, {
action:'requestInfo',
param:['Model S Battery', '1337']
});
//看车
execute(TeslaSalesControl, {
action: "arrangeViewing",
param: ["Model S", "123"]
});
//买车
execute(TeslaSalesControl, {
action: "buyVehicle",
param: ["Tesla 3", "23243425"]
});

对比Redux

  • Store == Receiver
  • Action == Command
  • Dispatch == Execute

让我们重新实现一下上面的例子

Store 在Redux中被拆解到各个Reducer当中进行初始化,这些Reducer都是纯函数,当被调用的时候就返回一个新的state,而不会导致别的奇怪的事情。这使得代码高度可预测和可测试性。
Store (Receiver)

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
import { combineReducers } = 'redux';

function arrangeViewing(state, action) {
switch(action.type) {
case "ARRANGE_VIEWING":
const { model, id } = action.data;
return `${model} and ${id}`
default:
return ""
}
}

function requestInfo(state, action) {
switch(action.type) {
case "REQUEST_INFO":
const { model, id } = action.data;
return `${model} and ${id}`
default:
return ""
}
}

function buyVehicle(state, action) {
switch(action.type) {
case "BUY_VEHICLE":
const { model, id } = action.data;
return `${model} and ${id}`
default:
return false
}
}

const rootReducer = combineReducers({
arrangeViewing,
requestInfo,
buyVehicle
});

export default rootReducer;
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import ReduxThunk from 'redux-thunk';
import rootReducer from '../imports/client/reducers/rootReducer';

// create a logger

const logger = createLogger();
const middleware = [ReduxThunk, logger];

const Store = createStore(rootReducer, {}, applyMiddleware(...middleware));
export default Store;
Action (Command)
const sampleActionObject = {
type: "BUY_CAR",
data: {
model: "TESLA",
id: "1234"
}
}
Dispatch (Executor)
import Store from 'store';

Store.dispatch({
type: "ARRANGE_VIEWING",
data: {
model: "Model S",
id: "123"
}
});

Store.dispatch({
type: "REQUEST_INFO",
data: {
model: "Model S Battery",
id: "123342"
}
});

Store.dispatch({
type: "BUY_VEHICLE",
data: {
model: "TESLA 3",
id: "23243425"
}
});

看到了吧,很像!设计模式可以让你更好的理解架构的本质。