前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >接到新需求时,从何开始设计?

接到新需求时,从何开始设计?

作者头像
JavaEdge
发布2023-01-11 11:23:02
3360
发布2023-01-11 11:23:02
举报
文章被收录于专栏:JavaEdge

即便我们能够极尽所能把代码写整洁,规避各种坏味道,但我们小心翼翼维护的代码,还是可能因为新的需求被破坏。

新的需求总会在路上,所以,写代码时需要时时刻刻保持嗅觉。

实现驳回

有个功能,内容作品提交之后要由相应的编辑审核。 审核有审核通过和不通过,这是系统中早就开发完成的。

有一天,新的需求来了:驳回审核通过的章节,让作者重新修改。造成作品需要驳回的原因有很多,比如,审核标准的调整,这就会导致原先通过审核的作品又变得不合格。

先看代码库已有怎样的基础。

首先,系统已有审核通过、审核不通过的接口。

代码语言:javascript
复制
PUT /chapter/{chapterId}/review
DELETE /chapter/{chapterId}/review

该设计将章节(chapter)的审核(review)当作了一个资源。 创建章节时,章节的审核状态就创建好了:

  • 审核通过 相当于对这次审核进行了修改
  • 审核不通过 相当于删除了该资源

对应这俩接口的服务接口:

章节上有个状态字段,标识现在章节处于什么样的状态:

  • 待审核
  • 审核通过
  • 审核不通过

已知这些基础,那驳回的需求如何设计?

新增一个驳回功能,自然要:

  • 新增一个驳回接口
  • 然后,在服务中增加一个驳回服务
  • 最后,在状态中增加一个驳回状态

看起来很合理,要准备写代码了呢。

这里有个坏味道来自要新增一个接口。

来一个新需求,就增加一个新接口,对大部分同学,这是一种多么正常的编程思维呀。 但必须对新增接口保持谨慎

接口,是系统暴露出的能力,一旦一个接口提供出去,你就不知道什么人会以何方式使用该接口。

很多系统有大量接口,仔细梳理会发现,有很多接口提供相似功能,这会让新人懵逼。即便你打算对系统进行重构,当清理掉一个你以为没人用的接口,就会有人跑出来告诉你,该接口调整影响了他们业务。

所以,必须对接口调整尤其慎重。最好从源头就开始限制,当我们想对外提供一个接口时,扪心自问:真的必须要提供新接口吗?

我面对该需求的第一反应和大多数人一样,也是新增接口。但是否真的要新增一个接口? 如果新增接口,就要复用已有接口,但复用前提是:新增的业务动作可通过已有业务完成,或是对已有业务进行微调就可以。

到底是新增or复用,还是要回到业务。

原业务中:

  • 审核通过会进入下一阶段
  • 审核不通过,就退回到作者那,进行修改

那驳回后呢?也会要求作者去修改。

发现了吧?驳回的动作和审核不通过的后续操作一致,只是起始状态:

  • 若原来状态是待审核 经过一个审核不通过的动作,状态就变成审核不通过
  • 若原来状态是审核通过 经过一个驳回动作,状态就变成驳回

所以,完全可复用原来的审核不通过接口。

既然是复用接口,所有的变化就都是内部变化了,可根据章节的当前状态判断,设置相应状态。 代码上,既不需要新增驳回接口,也无需新增驳回服务,只需修改 Chapter 类的内部,改动量比预期的小了很多。 代码结构如下:

这样,只需增加一个驳回状态,在当前状态是审核通过时,赋值这个新状态。 看来,已经把这次要改动的代码限制在一个最小范围。

但真的需要这么一个状态吗?

是否增加一个驳回状态,回答这个问题还是要回到业务上、:

驳回后续的处理与审核不通过的状态到底有何不同?

按PM本来的需求,他是希望做出一些不同。比如:

  • 审核不通过状态,编辑端则无法查看
  • 处于驳回状态,编辑则可以查看

但在当前产品状态下,是否可统一二者呢?即都按审核不通过处理?

PM想了想,觉得其实也可以。于是,两种不同状态在这里得到统一,最后根本没必要新增这个驳回新状态。

最终,这次的业务调整,后端服务代码没做任何修改,只是前端在需要驳回时,增加了一个对审核不通过的调用,而所有这一切的起点,只是我们对于新增接口的嗅觉!

定时提交

一般作者写完一章后,就直接提交,这是系统已有功能。 现在有个新需求:有时,作者会囤稿,为保证自己每天都能有作品提交,作者希望作品能在自己设定的时间提交,即一个章节在它创建时,并不会直接提交到编辑那里去审核,而等到特定时间再完成作品的提交。

“每天都有作品提交”本质就是一种连续的签到,通常系统都会给连续签到以奖励,这也是对于作者的一种激励手段。

那么,你会如何实现该需求?

与这个需求最直接相关的代码就是章节信息:

代码语言:javascript
复制
class Chapter {
  // 章节 ID
  private ChapterId chapterId;
  // 章节标题
  private String title;
  // 章节内容
  private String content;
  // 章节状态
  private Status status;
  // 章节创建时间
  private ZonedDateTime createdAt;
  // 章节创建者
  private String createdBy;
  // 章节修改者
  private String modifiedBy;
  // 章节修改时间
  private ZonedDateTime modifiedAt;
  ...
}

要实现这个需求,需要一个定时任务,定期扫描那些需提交的作品。

但这些定时的信息放在哪?

这还不简单,在章节上加上一个调度时间不就行了:

代码语言:javascript
复制
class Chapter {
  ...
  private ZonedDateTime scheduleTime;
}

这么实现并不复杂。但这可能也是坏味道,因为要改动实体。

一有需求,就改动实体,这几乎是大部分开发者条件反射的习惯。 然而,对于一个业务系统,实体是最核心的,改动之须谨慎考量。

因为随意修改实体,必然伴随其它部分调整,经常变动的实体,会让整个系统难以稳定。 系统的业务一般不会经常改变,所以,核心的业务实体应该是一个系统中最稳定的部分。

你可能会说:我有什么办法,需求总在变,就总会改动到这个实体呀!

需求总在变,这没有错,但是否真的就要改动业务实体? 很多时候,这只是应有职责没分析清楚而已,写代码从不考虑更好的设计!

我们现在需要的是定时提交一个章节,而这个定时信息并非核心业务实体的一部分,只是在一种特定场景下才需要的信息。 所以,它根本不该添加到 Chapter 类。

那应该放在哪呢?

显然,这里少了一个模型,关于调度的模型。 只需新增一个模型,让它和 Chapter 关联(组合):

代码语言:javascript
复制
class ChapterSchedule {

  private ChapterId chapterId;
  private ZonedDateTime scheduleTime;
  ...
}

这样,后续再有关于调度的信息,即可放至该模型里。而且核心模型 Chapter 保持不变。

把定时提交的信息与章节本身分开,是因为这二者的改变原因不同。将二者混在一起,就违反了单一职责原则。

看来已经得到很合理的方案了,有了基础数据结构,修改对应接口和服务都易如反掌了。

但这就结束了吗?

新增的需求是定时发布,有这么个需求,和作者激励有关。 要想确定作者的激励,就要确定章节的提交时间,但如何确定章节提交时间?

在原来实现中,创建时间就是提交时间,因为章节是立即提交的,而现在创建时间和提交时间有可能不同了。

你可能会想到,创建时间不行,那就用修改时间。我告诉你,这也不行,修改时间是章节信息最后一次修改的时间,它有可能因为各种原因变更,最简单的就是编辑审核通过,这个时间就会变。

至此,突然发现,模型里居然没有存放提交时间的地方。 是的,得修改实体了,给它增加一个提交时间:

代码语言:javascript
复制
class Chapter {
  ...
  private ZonedDateTime submittedAt;
}

肯定有读者好奇了:之前讨论那么多,不就为了不在 Chapter 新增信息,你现在就这么轻易新增字段了?

一个字段该不该加在一个类里,取决于其改变原因:

  • 定时时间确实不该加
  • 这里的提交时间却应该加 提交时间本就是章节的一个属性,只不过之前,这个信息与创建时间共用。如今,因为定时提交的需求,二者应该分开了

难道不能直接用 submittedAt 去存储调度时间?

严格地说,不行。因为调度时间可能与具体提交时间有差异。 比如,因为某种原因,系统宕机了,启动后,调度任务执行,这时可能已经过了调度时间很久了,但这时提交章节,它的时间就不会是调度时间。

还记得为什么要做这个分析吗? 因为要改动核心实体,而这就是一个坏味道高发区。

总结

新需求到来时需要关注:

  • 增加新接口
  • 改动实体

接口和实体,也是一个系统对外界产生影响的重要部分,一个是对客户端提供能力,一个是产生持久化信息。所以,我们必须谨慎地思考它们的变动,它们也是坏味道产生的高发地带。

对于接口,我们对外提供得越少越好,而对于实体,必须仔细分析它们的定位。

谨慎地对待接口和实体的变化。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/01/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实现驳回
  • 定时提交
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档