January 2, 2021
By: Kevin
怎么避免在代码里搞飞机✈
假如说一个页面按钮点击的时候需要做如下5个检查, 每个检查会报告不同的错误msg.
(defn- valid1? [m]
(->> m ...))
(defn- valid2? [m]
(->> m ...))
(defn- valid3? [m]
(->> m ...))
(defn- valid4? [m]
(->> m ...))
(defn- valid5? [m]
(->> m ...))
我们最常见的写法: 逐层嵌套的if语句, 代码的形状是一个典型的大于号 >. 貌似一架飞机.
其实任何语言多次if/else嵌套之后都是飞机状态. 类似代码, 我们不少人写过吧...
[:> Button {:on-click #(if (valid1? m)
(if (valid2? m)
(if (valid3? m)
(if (valid4? m)
(if (valid5? m)
(ok-action!);;;;; ====> 我是✈️头
(error "5没有通过"))
(error "4没有通过"))
(error "3没有通过"))
(error "2没有通过"))
(error "1没有通过")
)} "ok"]
就是得写很多的分支, 会有点烦.
灵活组织数据结构 + 使用some函数, 以下的写法更加简洁很多.
(defn- all-checks [m]
(some (fn [[f msg]]
(when-not (f m) msg))
[[valid1? "检查1没有通过"]
[valid2? "检查2没有通过"]
[valid3? "检查3没有通过"]
[valid4? "检查4没有通过"]
[valid5? "检查5没有通过"]]))
[:> Button {:on-click #(if-let [msg (all-checks m)]
(show-error msg)
(ok-action!))} "ok"]
此外, 如果对OOP的设计模式熟悉, 你会发现这是一个典型的责任链模式.
some函数, 完成了这个模式对应的全部功能
下面是这个模式的Java实现:
先定一个抽象的Validator:
public abstract class Validator {
protected Validator nextValidator;
abstract void process(String message);
public void setNextValidator(Validator nextValidator) {
this.nextValidator = nextValidator;
}
}
然后定义具体的Validator
class Validator1 extends Validator {
boolean valid (Message msg) {
....
}
@Override
void process(Message msg) {
if (valid msg) {
if (nextValidator != null) nextValidator.process(message);
} else {
return "检查1没有通过"
}
}
}
class ProfanityValidator extends Validator {
boolean valid (Message msg) {
....
}
@Override
void process(Message msg) {
if (valid message) {
if (nextValidator != null) nextValidator.process(message);
} else {
return "检查2没有通过";
}
}
}
class RejectValidator extends Validator {
boolean valid (Message msg) {
....
}
@Override
void process(Message msg) {
if (valid message) {
if (nextValidator != null) nextValidator.process(message);
} else {
"检查3没有通过"
}
}
}
// 后面两个省略了....
最后应用这些具体的检查过滤
Validator validator1 = new Validator1();
Validator validator2 = new Validator2();
Validator validator3 = new Validator2();
Validator validator4 = new Validator2();
Validator validator5 = new Validator2();
validator1.setNextValidator(validator2);
validator2.setNextValidator(validator3);
validator3.setNextValidator(validator4);
validator4.setNextValidator(validator5);
Message message = ...
validator1.process(message);
比较了三种方式
- 纯粹的if/else 会搞飞机
- 使用clojure的some函数实现责任链模式
- 原始的责任链模式
同样是链式处理的场景, FP比OOP要简单很多.