新需求与架构设计的演进

在前面的二十天中我们完成了基本需求,但是这样的进度在真实的专案中只是刚开始而已,有可能到目前为止做的只是 prototype,大家玩了一下觉得很喜欢,想要将他变成更完整的 App 再上架,又或是这是一个 MVP(minimum viable product),而且已经上架经过市场验证,想要再加更多功能以符合使用者的期待。不管如何,在功能越来越多的情况下,核心逻辑会越来越复杂,原本的程序架构已经支援不了新的需求,架构设计的改变是势在必行的。

当然这时候一定会有不同声音出现,对於产品这边的角色当然希望功能会快出来越好,对於工程这边的角度则是希望新功能尽量不要牺牲开发的品质,这两边的角色通常是站在对立面,牺牲掉任何一边都不是好事。对於开发来说,最怕产品端朝令夕改,今天晚上提的需求明天就要,一切彷佛没有原则可言,一个奇怪的需求就要使得原本规划好的架构大改。那产品端怕什麽呢?怕开发跟他们说这个做不到,架构要大改,至少要花两个月的时间重构才能开发这个新功能。

其实这里有个很有趣的一点,就是“架构”。工程师很常犯一个毛病,喜欢设计一个厉害、爲未来一切都规划好、自己很满意的架构,抽出未来需要重用的函式库或元件,做着 infrastructure 层级的工作。然而过了一年後一切不如所愿,自己的规划的共用元件无法在新功能派上用场,很多写好的,放在一旁的函式库从未被使用,自己非常满意的 infrastrcture 元件限制了新公能的开发。时间久了,当功能越来越难开发时,通常到了这时候,那个大家都不想面对的事情就要发生了,那就是 - 整个专案需要“打掉重练”。

在这铁人赛的最後一部分,我将新增一些新需求,而这些新需求会打破原有的设计,原来的设计的限制无法达这些新需求。因此我们需要一个新的架构,但是这个架构不应该随时需要大幅度的修改,他会跟着产品的演进而一起成长,进而同时满足产品端与工程端人员的期待。

便利贴第三部

先来复习一下前面两部各完成了什麽

第一部:显示便利贴、使用手势移动便利贴、同步云端资料

第二部:完成新增、删除、修改颜色、编辑文字这四个功能

第三部分我们将要新增功能是:放大、缩小以及平移整张白板,这是一个非常巨大的改变,因为放大缩小就代表了显示的便利贴数量再也不是固定的,从十几个到上百个便利贴都有可能,再加上每个人都有可能拥有属於他们的编辑区域,因此随时取得全部便利贴的更新是一件非常浪费效能的事。

手势操作也是另外一个问题,目前拖曳便利贴可以直接在未选取状态的便利贴上进行,平移白板的这个功能就因此变得不太可行,因为使用者很有可能是想平移整张白板,但是却不小心移动到一张便利贴,使用者体验变得不是很好,所以这部分需要跟产品端做沟通,有时候他们不会第一时间就意识到这个问题的存在(虽然这个问题很明显,但现实上就是会有做了才会发现的问题),主动提出问题并一起想出一个好的解法是工程端这边的责任。最糟糕的是自己默默想出解法,做了两个礼拜之後 Demo 给大家看,才发现跟产品端的人的想像差了十万八千里,到那时候就已经浪费掉太多时间了。

再来是显示顺序的问题,也就是哪一张便利贴该在上面,哪一张便利贴该在下面,这个通常来说也不会是一个产品端容易发现的问题,他们只要看到画面没问题就好了,但是对於开发这边,要是没有一定的上下顺序的话,就有可能在每次移动便利贴时都要重新调整顺序,而这个顺序也没有特别的演算法或依据,就只是依照 Firebase 给我们便利贴的顺序来显示,至於 Local 更改过的资料怎麽办呢?为了不破坏掉原有的顺序,只好选择采用“取代”的方式来保持原有的顺序,在之前的实作里我就是这样做的:

override fun getAllNotes(): Observable<List<Note>> {
    return Observables.combineLatest(updatingNoteSubject, allNotesSubject)
        .map { (optNote, allNotes) ->
            optNote.map { note ->
                val noteIndex = allNotes.indexOfFirst { it.id == note.id }
                allNotes.subList(0, noteIndex) + note + allNotes.subList(noteIndex + 1, allNotes.size)
            }.orElseGet { allNotes }
        }
}

这段程序码是在 FirebaseNoteRepository 里的,在这段程序码的第六行中,为了确保显示的顺序是对的,我拆掉原有的 List 将它分成两段,然後再把相对应的 note 安插在中间,就可以不用担心每一次新的资料的顺序有改变而影响显示的顺序。

像这样的实作就是一种 "Workaround" ,虽然暂时解决了问题,但是没有意识到更根本的原因是什麽。为了解决这个问题,我们可能会在 Note 里新增一个 z 的栏位,然而这个 z 的新栏位是工程端做的决定,但是产品端从头到尾都没有被通知到这个决定,也无法参与讨论,当之後产品端发现并提出不同想法时,但是这个想法跟目前的机制差太多,这时候要改就是难上加难了。

小结

相信在一间正常的公司里,我们都希望产品能够成功,在现实生活中,重写架构应该都是一件大家都不愿意看到的选项,而且一但这麽做,很容易过个几年,就会觉得上一次设计的架构有够难用,又整个重写一次。因此进行架构的设计应该要与产品的“形状”一致,而这样设计出来的架构,会很大部分的符合产品未来的走向,所以未来加新功能会很快,产品端很满意,工程端也因为设计出可高度重用,好维护的架构而获得成就感,大家都开心!

注:以上说的这些当然很不容易达到,最重要的还是需要“人”的配合,有好同事都是上辈子烧好香换来的,要是再考虑这因素就不是本系列文的重点了,在这系列文章我将忽略“人”这个因素,假设大家都是很好沟通的好同事为前提。


<<:  Day.18 InnoDB资料储存 - 主索引架构 (Clustered Index)

>>:  [Day25] Tableau 轻松学 - TabPy 使用方法 2

使用Visual studio code (vscode) 一键执行编译C++ compiler 超简单

**1.安装Visual Studio code ** 有安装的可跳过这一步 官网连结: http:...

样式属性

错误版 正确版 比对两个,发现哪里有bug了吗? 对就是,鼠标移开後,样式应该变回原本的,但它没有。...

4套最佳 Instagram 影片下载器-PC端〖2022亲测〗

作为最受欢迎的社交媒体平台之一,Instagram 绝对是在线分享媒体的最佳场所,例如照片、视频、直...

django新手村2 ------创建models

上一篇提到 主urls->次urls->views->models->vie...

【这些年我似是非懂的 Javascript】Day 27 - 物件 # Part 3 # 特性描述器

今天要主要来分享一下特性描述器。 特性描述器 (property descriptor) 在 ES...