从一次快捷键关联改动引发的思考
最近工作中,遇到一个快捷键相关的问题,在修改这个问题的整个过程中,经历多次思考、讨论和回顾,对此问题以及对应解决思路的演进有较为深刻的体验,在本文中进行完整记录,已沉淀经验。
问题背景描述
当前系统有2类账户,以下称为账户A和账户B,A\B账户下具有相同的业务功能,以下简称为查询功能。用户在修改B账户的查询功能快捷键,提示修改无效。修改A账户的查询功能快捷键功能是正常。
问题分析
A、B账户下的查询功能,复用的是同一份代码。因之前设计失误,导致A、B账户下的查询功能Id以及快捷键是不同的,在使用时会被认为是两个独立的功能。之前没有问题的原因是因为查询功能只在A账户下有,后来有新需求,B账户也要增加该项功能,具体在增加时,简单的复制A账户下的配置到B账户下,导致该功能在A、B账户下的功能Id一样,这就给后续操作买下隐患。
软件在初始化时是以功能Id为key来读取,如果遇到2个功能Id一样的key,只会以第一个为准。因此,在修改B账户下的查询功能时,修改后的值不会被读取,自然也就无法生效。
解决方案分析
最开始思索解决方案时,想着以改动最小的思路去解决,将B账户下的查询功能Id设置为不一样,即可解决这个问题。改动范围小,只影响特定功能。
在和同事讨论之后,如果按照目前的改法,是沿着之前不好的设计思路继续前进,这只会解决目前看到的问题,而没有彻底解决A/B账户下同一个业务快捷键不同,这个设计本身的问题。这会导致越往后改会越繁重,属于只解决表面现象问题的解法。就像自己之前脑海里想的问题触发链条:
问题根源 -> 链条1 -> 链条2 -> ... -> 链条N -> 问题具体表现
越靠近问题具体表现去修改,改动越小,但问题根源没有改动,本次修改只堵住了这条链条,根源未改,后续随着需求的演化,还会有其他分支出问题。这种,头痛医头脚痛医脚的修改思路是短期行为,但工程上和实现上是需要权衡。从已有的工作经验来看,越靠近问题具体表现那层,改动以及影响的范围越少,越靠近问题根源,改动以及影响的范围相对越多。越往上追溯,涉及到的依赖者越多,改动的可控性以及能否一次改好并且不会由此引发其他关联问题,这需要更多的分析以及测试用例覆盖。特别是在邻近版本发布时间窗口,如果不是特别重要、紧急的Bug,可以先缓一缓或者采取影响范围面小的解决措施,同时加以备注,需要在下一版中,采取更为彻底有效的解决方式。
下面以上述问题为例子,进一步更深层次去解决并完善已有设计。
A、B账户下的查询功能支持链接属性。既支持B账户的查询功能链接到A账户的查询功能,两者公用同一功能Id以及其他属性。在初始化时,针对链接节点进行特殊处理,从被连接的节点拷贝相关属性到链接节点上。
上述设计可以解决相同功能在不同账号下的属性不一致问题,但也带来了新的问题,修改功能属性时的关联改动。
修改功能属性,具体可细分为以下三种情况:
- 只修改非链接节点,例如A。
- 修改A下的查询功能,预期B下的查询功能会一并修改。
- 同时修改非链接节点和链接节点,例如A,B。
- 同时修改的,以A为准,还是B为准,还是以哪方最后修改为准,这需要和产品讨论后决定。
- 只修改链接节点,例如B。
- 修改B下的查询功能,预期A下的查询功能会一并修改。
在增加链接属性的设计下,场景1自然而然的实现了,但场景2和3还无法支持,需要额外处理。下面就解决场景3下的同步修改,给出具体思路分析。
实现思路分析
通过上述分析,大的方案方向确定的,各种场景也区分开了,下面就具体分析场景3的实现思路,目的是为了修改链接节点时,同步修改回到被链接节点上。
经过与同事多次讨论、分析,大的解决思路有两个方向:应用层和数据层,下面分别来阐述。
应用层实现方案
从应用层去实现链接节点修改的同步逻辑,分析需要多次分离和查找,实现较为复杂。在该版实现中,对于场景2的解决,是按被链接节点为主。在这版实现中,考虑了场景2和场景3,需要新增许多辅助代码,逻辑复杂。
这种实现思路的复杂性在于,从应用层的角度来看,链接节点在初始化后,其内容是被链接节点数据的副本,他们是两个完全独立的功能,只不过属性内容是一模一样的。链接属性只用在赋值,没有指向关系。因此,在实现同步写回时,需要全局遍历去寻找对应的链接节点,然后将改动写回。
这里就有另外一种思路,除了链接属性外,增加链接父节点指针,这就是从数据层实现的方案。
数据层实现方案
从数据层视角去实现,增加链接节点父指针,在初始化时就设置指向对象,后续在同步修改时,将同步时机前移,直接将链接节点的改动同步到父节点中,以此同时,抛弃掉链接节点自身的改动。这样,可大大简化同步流程,整个过程简洁清楚,一目了然。
需求层的解决思路
对于A、B类账号下共有的功能,对外只提供一个修改入口,不提供多个可见入口,这样,在需求层面上,就回避了链接功能的关联修改问题,这种改法需要和产品讨论再做决定。
小结
在软件开发中,每一个需求都有且应该有多种设计方案,每种设计方案下,又有多种实现方式。
有时在提出解决方案后,除了它预期要解决的问题外,可能会隐含地引出其他问题,这些问题在前期讨论时是很难预见全的,只有具体实现时,才会碰到。因此,在每得到一个解决方案后,可以先进行小步尝试,发现一些在方案讨论阶段没有考虑到的场景或者路径,再进一步反馈完善。
警惕以下这些懒惰心态:
- 别人现在是这么写,所以我现在也这么写
- 别人以前是这么写,所以现在我也这么写
- 我以前是这样写的,所以现在我继续这样写
多思考,多讨论,多阅读已写过的代码,在不断的否定、质疑、讨论中,完善已有想法或者解决方案的思路,可从应用层、数据层、需求层面去扩展思路,在没想清楚所有分支和可能的错误之前,不要贸然动手改动。