S会议开发手札:过渡设计

今天打算来专门聊一聊过渡设计的事情。

过渡设计可能对每个程序员来说都不陌生。基本上,凡是设计一些不知道现阶段该怎么用的东西,都属于过渡设计。

过渡设计有可能会隐藏在一种听起来很合理的理由背后出现:我们应该设计成的样子。为了这个想象中的样子,最终会定下很多过于理想的需求,这些需求,就是过度设计的体现。 大家平时讨论产品的时候,尤其是有产品经理参与的情况下,很容易就陷入“理想中的样子”的怪圈。大家说起某个功能应该有的样子的时候,都会很兴奋,不由自主把需求不断泛化扩展,觉得如果做成这样,这功能就无敌了!不过,在一个目标还不确定,真正的客户没有体现出这种需求的时候,就预先实现一些相关概念,显然是不值当的。

这次的开发,遇到的过渡设计主要有:

  • 时间的表示

    为了追求能灵活表示时间,不仅显示的时候没有使用标准的时间表示而是用了更加口语化的表述,而且还加入了所谓模糊时间的概念。比如早餐,午餐这种模糊时间以及周末这种模糊日期。当时时间前前后后争论了大概有一周,才算定下了一些输入输出的例子,而这些例子一方面要用不同语言都实现一遍,另一方面也为如何输入时间造成了麻烦。现在的时间实现无法直接利用任何现有基础库来完成转换,或者本地化,而这种灵活时间并没有给项目带来可见的优势,常见的日期输入还是以标准日期为主。倒是today这个模糊日期被命中率很高。

  • tag的滥用

    在设计新的地图时,为了能灵活表现地标的一些属性,设计了tag这个属性。有些tag会有特殊的属性,比如在一张地图里某个tag会有唯一性,或者会同步一个属性。而实现时,由于tag无法随机访问,每次查看是否具有某些属性,需要遍历所有的tag。一方面遍历效率不高,另一方面一些唯一性的逻辑难以实现,唯一性只能把所有地标取出来,然后遍历来确认是否某个tag唯一。一个地图上地标不多,所以这个操作倒是目前还没有性能问题(当然,也没多少用户),但是显然从设计上,是没有什么扩展性可言的。

  • 复杂的失败机制

    在app上,有可能遇到因为网络问题造成的提交失败。一般来说,处理这种失败有两种办法:简单的话,同步提示失败,然后数据恢复到没提交的状态;复杂的话,缓存失败的请求,在网络恢复时再次提交。后一种有很多事情需要考虑,比如再提交时,多个请求需要保证提交的顺序与发起时的顺序一致,需要区别对资源的修改请求和获取请求,而如果要修改的数据与数据源的数据不一致,那么是否要提交这次修改就又面临很多选择的问题。基本上,可以把后者看成一种分布式版本管理库,需要处理本地分支和合并。从使用上讲,普通用户是否是git的目标用户?至于实现难度……反正这次开发选择了后者。

  • 过早泛化widget

    widget其实也是一种类似tag的泛化机制,比tag要多出很多行为和数据存储。而实际开发过程中,随着项目切入点的变化,到底给用户显示的是某个widget,还是容纳widget的容器,也一直变来变去。而为了配合前端获取数据的效率,一些widget的数据就变成不得不附加在整个widgets所在的容器,递交给前端去处理。而这种请求,不仅加剧了请求容器时需要处理的请求数量,还会导致在请求widget数据时,widget有可能还会返回去请求整个容器的一些属性。

  • post

    post最早设计思想是一种即时贴的感觉,可以贴到任何对象上。嗯,我说完这个就觉得这东西好可怕,因为这个附加的对象只是一段字符,而被贴的对象要对外暴露属性时,还要去查询一次post。当然,最后这东西也只用在了conversation一个对象上。

  • 消息传输机制

    理想中的bus,多机互联互通,服务自动发现,会自动就近存取数据,自动路由,消息交换成本低,速度快,甚至带有队列和失败机制。我觉得我要能写出这个东西,我就去发个paper,然后等别人引用我的paper,就能了此一生了。不过这个功能,最初讨论的时候我也很兴奋,觉得有了这个就能实现很多自动管理的机制。也许当初讨论的时候更加现实一些会更好。或许,等我们真的有了多台机器的时候,再考虑这些,就能找到更明确的需求了。

Googol Lee

多年生软件工程师,信仰开源

Munich, Germany http://air.googol.im