写代码的程序员许多,写好代码的程序员却不是那么多(愿你我都在路上)。今天共享一个避免很多if-else
的案例,和咱们共同进步。
该篇是笔者在
Flutter
项目中遇到的问题,所以示例代码是Dart
语言。请读者不要有担负,语言不是重点,重点是思想。
问题的由来
下面是两段实际的业务代码:
-
购买
操作,再履行真正的购买流程之前,需求有必要的条件校验_onBuyButtonClick() { /// 1. 用户封禁校验 final user = getUserInfo(); if (user.isForbidden) { showForbiddenDialog(); return; } /// 2. 未付出订单数量校验 final orders = getUserWaitingPayOrders(); if (orders.length >= limit) { showTooMuchOrderWaitingPayDialog(); return; } /// 3. xxx /// 4. xxx /// 购买流程 }
-
出售
操作,再履行真正的出售流程之前,需求有必要的条件校验_onSellButtonClick() { /// 1. 用户封禁校验 final user = getUserInfo(); if (user.isForbidden) { showForbiddenDialog(); return; } /// 2. 店铺封禁校验 /// 3. xxx /// 4. xxx /// 售卖流程 }
这样需求校验的流程,咱们一共有10个
!每个流程需求校验的项目2~5
个不等。咱们体验下需求文档:
这儿的每一张图代表一个流程,每个黄色方块是一个校验项目。当然,流程是不可能重复的,但每个校验项可能是重复的。所以咱们能够将问题笼统为:如安在N
个操作加入M
个前置验证?例如:
- 操作
N1(购买)
需求查看M1(用户是否被封禁)、M2(等待付款的订单不能太多)...
- 操作
N2(上架出售)
需求查看M1、M3(店铺是否被封禁)、M4(正在售卖的商品是否达到数量上限)...
- 操作
N3
需求验证M5、M6、M8
….
关于这种非常合理的需求
,咱们怎么能辩驳呢? So, let’s kill it!
解决方案
Show me the code
。先给出现在正在运用的方案,再剖析里边的详细细节。
咱们终究完成的作用如下(以购买流程
为例):
_onBuyButtonClick() {
/// 运用CheckController来操控哪些条件是需求被查看的
final anyChecker = CheckController().check([
Requirement.account,
Requirement.orderBuffer,
]);
/// 若存在需求处理的,就处理它
if (anyChecker != null) {
anyChecker.handle();
return;
}
/// 之前的购买流程
}
能够看到,咱们将本来的几十行的校验代码(最长的8个校验项目,也就是8个if判别),缩短为短短的几行。相比之下,该方案有许多长处:
- 没有重复代码。之前
N2
流程中的校验代码,彻底是N1
的Copy
。现在即便两个流程具有相同的校验项,也只体现在枚举的相同case
上。 - 可读性增强,可维护性大大提高。在很多的
if-else
中搞懂它是做什么的,尽管不是很有挑战,但它的确需求必定时刻。特别是在一段时刻之后,加上没有详细注释的情况下。 - 可维护性大大提高。一个流程的校验项,彻底对应数组的元素,包括校验项的增删改查。假设在一个流程上改变两个项目的优先级,之前你需求读懂哪两个
if
是你关心的,然后才干调整。现在,你只需求在数组中找到对应的case
就能够。并且现在它们是肯定集合的,之前的代码可能一部分在屏幕可见规模,另一部分彻底不在!
怎么完成
假如你对上面的完成感兴趣的话,这儿咱们一起剖析它是怎么完成的。
第一阶段 – 减少重复性
若想复用M
个查看,咱们必须将查看部分的代码独立出来。以购买
的查看为例,咱们能够发现整个过程能够分为两步:
- 条件校验
- 成果处理
一切
M
个查看都能够看做,校验xxx
条件,不满足的话就xxx
。这儿咱们将每个查看封装成独立的类,以购买
中的用户是否被封禁
查看为例:
class AccountForbiddenChecker {
/// 依据条件回来用户是否被封禁
bool match() {
return false;
}
/// 用户被封禁的详细操作,如弹窗正告
void handle() {}
}
再比如等待付款的订单不能太多
的查看:
class OrderWaitingPayBufferChecker {
/// 判别用户未付出的订单是否太多
bool match() {
return false;
}
/// 未付出订单过多的详细操作,如弹窗正告
void handle() {}
}
像这样,咱们能够将这M
个查看,都封装在详细的类中。避免了多处流程条件查看中的复制粘贴。但关于运用者来说,他需求记住每一种Checker
的姓名,最起码需求有形象,这是一种担负。所以,咱们运用枚举,来表示每一个查看项:
/// 需求校验的项目
enum Requirement {
account,
orderBuffer,
// ...
}
由枚举到详细的查看类,咱们还需求有个转化过程。这儿运用了switch
:
extension Mapper on Requirement {
RequirementChecker toChecker() {
switch(this) {
case Requirement.account: return AccountForbiddenChecker();
case Requirement.orderBuffer: return OrderWaitingPayBufferChecker();
// ...
}
}
}
第二阶段 – 添加可复制性
当需求协调多个类的时候,咱们就需求一个管理者了。
/// 查看项管理器
class CheckController {
/// 依据传入的枚举,判别详细的项目是否匹配,若匹配,则回来对应的查看者。
RequirementChecker? check(List<Requirement> items) {
for (final item in items) {
final checker = item.toChecker();
if (checker.match()) {
return checker;
}
}
return null;
}
}
RequirementChecker
也是必要的,它是一个接口,负责标准化每个Checker
:
abstract class RequirementChecker {
bool match();
void handle();
}
然后每个详细的Checker
完成该接口,这样管理者,以及外部才干统一运用多个Checker
。
到这儿,咱们就完成了上面的解决方案。关于每个流程,咱们只需求CV
大法,然后对校验项稍作修正,即可达到作用。bingo
!
相关引荐
今天的解决方案并不是笔者初创。其思想来源于规划形式中的职责链形式
。墙裂引荐这个网站。
关于每种形式,都配有很多的图解、问题以及解决方案。例如职责链形式
一章:
简直不要太赞!
好了,秘籍都奉上了。希望咱们提前登峰造极!