多问“怎样才能做的更好”

写在前面

周末,播放器里的歌放到最大声。我盘着腿坐在我的小床上,望着窗外远方陌生的高楼;三年,真的是望不见的未来。

之前还在做业务开发的时候,有一天产品经理跑过来给我讲了一个好听的故事,故事听完了听懂了,我便开始做技术方案。不过,好听的故事的技术方案都不是那么容易做的。

本文的理解难度

入门——初级——中级——高级√,本文理解难度较高。

一个关于动物医院的需求

需求背景简介

在一个很大的动物医院(animal-hospital)里,有好几位德高望重的动物医生(animal-doctor),他们每天会给很多很多不同种类(animal-kind)的动物(animal)治病。现在需要开发一个系统来维护每位医生接待的小动物种类及其数量,从而方便跟踪、统计每位医生的工作情况。

功能列表

  1. 开发一个电子系统,实现:
  2. 当有特定种类的小动物送到医院的时候,从系统中可以知道当前负责的哪位医生,从而方便把小动物送过去医治;
  3. 能够通过系统查看任何一位医生当前持续接待的各个种类的小动物的数量;
  4. 能够查看任何一位医生历史中接待过的各个种类的小动物总数量(如果一位新医生接管另一位离职医生的工作,小动物的数量算到离职的医生那里);比如某医生历史上共医治过 23 只猪、100 只猫、68 只狗、2 匹马、33 只蜥蜴,等等。

具体的规则描述

出于各方面的考虑,动物医院有如下的规则:

  1. 每位医生同一时间负责最多不超过 3 个种类的动物。比如医生-A 当前负责了猪、狗两种动物,医生-B 当前负责了 马、鼠、猫三种动物,医生-C 当前负责 蜥蜴一种动物。
  2. 当有动物需要医治的时候,根据动物的种类送给特定的医生那里医治。比如根据第一条规则的情况,当有一只生病的小狗就医时,会直接送到医生-A 去医治;当有一只生病的小猫就医时,会直接送到医生-B去医治;依次类推。
  3. 某个医生持续接待的某个种类的动物超过 30 只的时候,会把当前医生所负责的这个动物种类按照一定的规则重新决策负责的医生。比如在某个时间段里,只要有生病的小狗被送到医院,都会被送到医生-A 那儿,持续这种情况,当小狗的数量超过了 30 只后,就不让医生-A 继续负责了。电子系统自动选出当前负责动物种类最少的医生-C(按照第一条规则的示例里,他只负责了 1 种动物),让医生-C 负责后面的小狗的医治。以后再有生病的小狗送到医院的时候,就会直接被送到医生-C 那里去,此时医生-C 持续接待 小狗的数量从 0 开始计数。
  4. 在以上规则实施的过程中,如果有医生离职,会有新的医生接替这位医生的工作,同时动物的持续接待数字归 0。比如医生-A 因为各方面原因不干了,那么医院会安排一位新的医生-D 来接替医生-A 的工作,假如这个时候医生-A 所接待的小狗还存在没有医治痊愈的情况,则由医生-D 负责为这些小狗进行医治。假设此时医生-A 已经持续接待了 10 只小狗,当医生-D 接手工作后,这个持续的数字将归零,即重新从 0 开始计数。

动物医院的技术方案(数据表设计)

在充分理解了产品经理的需求以后,我和产品经理分头行动,他去做原型设计,我则负责技术方案的设计。

接下来就尝试着给上面的需求做一下技术方案,按照之前的文章,一个技术方案包含很多的内容,但是受到篇幅的限制,本文将侧重数据表的设计部分,其他的则不涉及(对大部分需求,数据表成型后很多东西就确定了🐽)。

第一种数据表设计——直观的数据表

根据产品经理讲的故事原型以及故事中的规则可以抽象出三个实体——具体就医的小动物、小动物的种类、小动物的主治医生,并能够在此基础上拟定出三张数据表——[Animal]、[Animal-kinds]、[Animal-doctors]、[Animal-kind-logs]——分别用来保存每个实体的数据( [Animal-kind-logs] 是 [Animal-kinds] 的日志表,后者用来记录前者的变更)。

  1. 当每只小动物来就医时,根据其种类到 [Animal-kinds] 中查找当前此类动物该由哪位医生负责,从而在 [Animal] 表中给相应的字段置位。
  2. 同时修改 [Animal-kinds] 表中对应记录的“持续就诊的数量”(加 1),然后判定这个值的大小;如果“持续就诊的数量”值小于等于 30 ,则不做任何改变;
  3. 如果“持续就诊的数量”值大于 30,则需要重新在所有活跃的医生中挑选一个医生,把 [Animal-kinds] 表中对应的记录的 “当前主治医生” 修改为选中的医生。
  4. 给动物种类挑选医生时,在 [Animal-kinds] 表中按照“当前主治医生”字段来分组统计,得到活跃的医生及其负责的种类数目,然后找出当前负责的动物种类数目最少的医生(假如存在条件一模一样的多个医生,随机挑选一个)。
  5. 有动物医生离职时,① 首先要修改 [Animal-docters] 表中该医生的状态,② 然后在该表中添加一条新的记录(新的医生),③ 把 [Animal-kinds] 中所有涉及到的记录的 “当前主治医生” 设置为新的医生;④ 把所有这些记录中的“持续就诊的数量”值设置为 0。
  6. 为了让挑选医生医生人员变动这两种情况可追溯,考虑在修改 [Animal-kinds] 表中“当前主治医生”字段时,在 [Animal-kind-logs] 表插入相应的记录。
  7. 通过 [Animal-kinds] 可以实时查询每个医生持续接待的各类小动物的数量。
  8. 可以通过 [Animal] 中的记录来统计每位医生医治的各个种类的小动物总数量。

通过情景模拟,可以知道上面的数据表设计能够满足需求以及故事中的规则。逻辑推演是自恰的,因此可以基于这个设计完善技术方案,继而完成开发。

虽然如此,但是总觉得方案中存在别扭的点,比如上面 a) 第 4 步给动物种类挑选医生时,要一次次对 [Animal-kinds] 表进行 group (分组统计)的操作;b) 第 5 步需要修改 [Animal-kinds] 中所有 涉及的记录的“当前主治医生” 的值。

按照以往的编码经验来看,设计方案中使人别扭的点往往会增加编码的难度,从而增加引入 Bug 的概率。那么有没有更好的设计方案呢?

另一种数据表设计——添加诊室表

添加诊室表后的故事表述

根据数据表设计的经验,为了解决上一小节提到的使人感觉别扭的点,可以考虑在 [Animal-docters] 和 [Animal-kinds] 中间引入一个诊室表 [Animal-clinics]用来进行优化。对应到原来的故事,就是引入“诊室”的概念,在不改变需求的前提下稍微修改一下故事的描述(可以和产品经理进行二次确认,避免理解偏差导致技术方案出现差错;因为大部分的故事是产品经理想出来以方便各方沟通的,在不改变需求的前提下换一种讲法也无伤大雅。)

  1. 每个诊室里可以包容一位医生坐诊;除非这个医生离职,否则坐诊医生不会改变。
  2. 每个诊室同一时间负责接待最多不超过 3 个种类的动物。
  3. 当有动物需要医治的时候,根据动物的种类分配到特定的诊室就医。
  4. 某个诊室持续接待的某个种类的动物超过 30 只的时候,会把当前诊室所负责的这个动物种类拿出来,并按照一定的规则重新决策负责的诊室。
  5. 在以上规则实施的过程中,如果有医生离职离开他所在的诊室,会有新的医生接替这位医生的工作入驻这个诊室,此时这个诊室负责的动物的持续接待数字归 0。

添加诊室辅助表后的逻辑推演

  1. 当每只小动物来就医时,根据其种类到 [Animal-kinds] 中查到当前此种类的动物由哪个诊室负责,从 [Animal-clinics] 中找到对应的记录后再对应地找到 [Animal-docters] 中的记录,从而在 [Animal] 表中给相应的字段置位。
  2. 同时修改 [Animal-kinds] 中当前记录的“持续就诊的数量”(加 1),然后判定这个值的大小;如果“持续就诊的数量”值小于等于 30 ,则不做任何改变;
  3. 如果“持续就诊的数量”值大于 30,则需要重新在所有的诊室中挑选一个诊室,把 [Animal-kinds] 表中对应的 “当前负责的诊室” 修改成为选中的诊室,同时更改 [Animal-clinics] 记录中两个相关记录的“负责接待的动物种类数值” 。
  4. 给动物种类挑选诊室时,根据 [Animal-clinics] 中的所有记录选择当前负责种类数目最少的诊室(假如存在条件一模一样的多个诊室,随机挑选一个),然后按照第 3 步所描述的处理。
  5. 有动物医生离职时,① 首先要修改 [Animal-docters] 表中该医生的状态,② 然后在表中添加一条新的记录(新的医生),③ 把相应诊室的坐诊医生改为新医生;④ 把 [Animal-kinds] 中对应记录的“持续就诊的数量”值设置为 0。
  6. 为了让挑选诊室医生人员变动这两种情况可追溯,考虑在修改 [Animal-kinds] 表中“当前主治医生”字段时,在 [Animal-kind-logs] 表插入相应的记录。
  7. 通过 [Animal-kinds] 可以实时查询每位医生(对应到每个诊室)持续接待的各类小动物的数量。
  8. 可以通过 [Animal] 中的记录来统计每位医生医治的各个种类的小动物总数量。

信息的组织方式

通过上面的讨论可以知道,存在不同的技术方案同时满足同一个需求列表。第一种技术方案基于对需求友好的故事,从产品经理的第一版故事直接进行设计;第二种技术方案对应的是对开发友好的故事,更利于开发人员设计代码逻辑,从而写出稳定可靠的代码。

目前的故事复杂度,还没有办法看出来哪一种技术方案更优,只是基于的故事不太一样,更进一步,是信息的组织方式不同而已,并没有本质的区别。不过我个人会倾向于选择第二种设计方案,因为我认为第二种设计方案的实践性和扩展性更好一些:① 让开发者容易理解,容易编码,能加快产品开发的速度;② 多抽出一层诊所的概念,解耦了医生动物种类 之间的关系,增加了故事复杂度的弹性。

小结

早几年,年轻的我问得最多的问题是“我哪里做错了”,喜欢较真;后来经历了失去与收获却发现,一段美好的感情,一个美好的前程,是需要多问“怎样才能做的更好”来追求的。

本文顺着这样的觉悟,对一个虚拟的动物医院的故事进行了两种技术方案的设计。并认为在做技术方案时应该遵循对开发友好的故事,从而利于开发人员写出稳定可靠的代码。当然,这需要花费一些心思,动一些脑筋😋。

最后,上面的两种方案中依然存在一个可以明显优化的点,大家可以找出来吗?

参考