我们的系统里有这样一个功能,内容作品提交之后要由相应的编辑进行审核。既然有审核,自然就有审核通过和不通过的情况,这是系统中早早开发完成的功能。
有一天,新的需求来了:驳回审核通过的章节,让作品的作者重新修改。造成作品需要驳回的原因有很多,比如,审核标准的调整,这就会导致原先通过审核的作品又变得不合格了。
在实现这个需求之前,我们先来看看代码库里已经有怎样的基础。首先,系统里已经有了审核通过和审核不通过的接口。
PUT /chapter/{chapterId}/review
DELETE /chapter/{chapterId}/review
在这个设计里,将章节(chapter)的审核(review)当作了一个资源。在创建章节的时候,章节的审核状态就创建好了。审核通过,就相当于对这次审核进行了修改,而审核不通过,就相当于删除了这个资源。
对应着这两个接口,就有两个对应的服务接口:
class ChapterService {
public void approve(final ChapterId chapterId) {
...
}
public void reject(final ChapterId chapterId) {
...
}
}
顾名思义,approve 函数对应着审核通过,而 reject 对应着审核不通过。相应地,章节上有一个状态字段,标识现在章节处于什么样的状态。章节是待审核、审核通过,还是审核不通过,就是通过这个字段标记出来的。
class Chapter {
private Status status = Status.PENDING;
public void approve() {
this.status = Status.APPROVED;
}
public void reject() {
this.status = Status.REJECTED;
}
}
好,我们已经知道了这些基础了,那驳回的需求该怎么设计呢?
既然增加了一个驳回的功能,那就增加一个驳回的接口,然后,在服务中增加一个驳回的服务,最后,再在状态中增加一个驳回的状态。这么做,听上去非常合理,你是不是已经按捺不住自己蠢蠢欲动的双手,准备写代码了呢?
且慢!我嗅到了一丝坏味道,这个坏味道来自于我们要增加一个接口。来一个新需求,增加一个新接口,对于很多人来说,这是一种常规操作。但我们必须对新增接口保持谨慎。接口,是系统暴露出的能力,一旦一个接口提供出去,你就不知道什么人会以什么样的方式使用这个接口。
我们常常看到很多系统有很多接口,如果你仔细梳理一番,就会发现,有很多接口提供类似的功能,这会让初次接触到系统的新人一脸茫然。即便你打算对系统进行清理,当清理掉一个你以为根本没有人用的接口时,就会有人跑出来告诉你,这个接口调整影响了他们的业务。
所以,我们必须对接口的调整慎之又慎。最好的办法就是从源头进行限制,也就是说,当我们想对外提供一个接口时,我们必须问一下,真的要提供一个新接口吗?
回到这个案例上,我们面对这个需求的第一反应和大多数人一样,也是增加一个新的接口。但是,是否真的要增加一个新的接口呢?如果不增加新接口,这就意味着要复用已有的接口。但复用的前提是:新增的业务动作是可以通过已有的业务来完成的,或是对已有业务进行微调就可以。
那么,到底是需要新增,还是复用,真正要回答这个问题,还是要回到业务上。
在原有的业务中,审核通过会进入到下一个阶段,而审核不通过,就会退回到作者那里进行修改。那驳回之后呢?它也会要求作者去修改。
说到这里,你就不难发现了,驳回的动作和审核不通过,二者的后续动作是一样的。它们的差别只是起始的状态,如果原来的状态是待审核,经过一个审核不通过的动作,状态就变成了审核不通过;而如果原来的状态是审核通过,经过一个驳回的动作,状态就变成了驳回。所以,我们完全可以复用原来的审核不通过接口。
既然是复用接口,所有的变化就全部都是内部变化了,我们可以根据章节当前的状态进行判断,设置相应的状态。
具体到代码上,我们既不需要增加驳回的接口,也不需要增加驳回的服务,只需要在 Chapter 类内部进行修改,代码改动量比原先预期的就小了很多。其代码结构大体如下所示:
class Chapter {
private Status status = Status.PENDING;
...
public void reject() {
if (status == Status.PENDING) {
this.status = Status.REJECTED;
return;
}
if (status == Status.APPROVED) {
...
.
}
}
}
按照这个理解,我们只要增加一个驳回的状态,在当前状态是审核通过时,将这个新状态赋值上去就可以了。看上去,我们已经把这次要改动的代码限制在一个最小的范围。但其实,我还想再问一个问题,我们真的需要这么一个状态吗?
是否增加一个驳回的状态,回答这个问题还是要回到业务上,驳回后续的处理与审核不通过的状态有什么不同。
按照产品经理本来的需求,他是希望做出一些不同来,比如,处于审核不通过的状态,编辑端是无法查看的,而处于驳回状态的,编辑是可以查看的。但在当前的产品状态下,我们是否可以将二者统一起来呢?也就是说,都按照审核不通过来处理呢?
产品经理仔细想了想,觉得其实也可以,于是,两种不同的状态在这里得到了统一,也就是说,我们根本没有增加这个驳回的新状态。
事情说到这里,你就会发现,在这次的业务调整中,后端服务的代码其实没有做任何修改,只是前端的代码在需要驳回时增加了一个对审核不通过的调用,而所有这一切的起点,只是我们对于增加一个新接口的嗅觉。
网友评论