通过重构得到更深层的理解

通过重构得到更深层的理解是一个涉及很多方面的过程。我们有必要暂停一下,把一些要点归纳到一起。有三件事情是必须要关注的:

(1) 以领域为本;

(2) 用一种不同的方式来看待事物;

(3) 始终坚持与领域专家对话。

在寻求理解领域的过程中,可以发现更广泛的重构机会。一提到传统意义上的重构,我们头脑中就会出现这样一幅场景:一两位开发人员坐在键盘前面,发现一些代码可以改进,然后立即动手修改代码(当然还要用单元测试来验证结果)。这个过程应该一直进行下去,但它并不是重构过程的全部。这里会在传统代码重构方法的基础上呈现了一幅全面的重构视图。

开始重构

获得深层理解的重构可能出现在很多方面。一开始有可能是为了解决代码中的问题------一段复杂或笨拙的代码。但开发人员并没有使用(代码重构所提供的)标准的代码转换,相反,他们认为问题的根源在于领域模型。或许是领域中缺少一个概念,或许是某个关系发生了错误。

与传统重构观点不同的是,即使在代码看上去很整洁的时候也可能需要重构,原因是模型的语言没有与领域专家保持一致,或者新需求不能被自然地添加到模型中。重构的原因也可能来自学习:当开发人员通过学习获得了更深刻的理解,从而发现了一个得到更清晰或更有用的模型的机会。

如何找到问题的病灶往往是最难和最不确定的部分。在这之后,开发人员就可以系统地找出新模型的元素。他们可以与同事和领域专家一起进行头脑风暴,也可以充分利用那些已经对知识做了系统性总结的分析模式或设计模式。

探索团队

不管问题的根源是什么,下一步都是要找到一种能够使模型表达变得更清楚和更自然的改进方案。这可能只需要做一些简单、明显的修改,只需几小时即可完成。在这种情况下,所做的修改类似于传统重构。但寻找新模型可能需要更多时间,而且需要更多人参与。

修改的发起者会挑选几位开发人员一起工作,这些开发人员应该擅长思考该类问题,了解领域,或者掌握深厚的建模技巧。如果涉及一些难以捉摸的问题,他们还要请一位领域专家加入。这个由4~5人组成的小组会到会议室或咖啡厅进行头脑风暴,时间为半小时至一个半小时。在这个过程中,他们画一些UML草图,并试着用对象来走查场景 。他们必须保证主题专家(subject matter expert)能够理解模型并认为模型有用。当发现了一些令他们满意的新思路后,他们就回去编码,或者决定再多考虑几天,先回去做点别的事情。几天之后,这个小组再次碰头,重复上面的过程。这时,他们已经对前几天的想法有了更深入的理解,因此更加自信了,并且得出了一些结论。他们回到计算机前,开始对新设计进行编码。要想保证这个过程的效率,需要注意几个关键事项。

自主决定。可以随时组成一个小的团队来研究某个设计问题。这个团队只工作几天,然后就可以解散了。这种团队没有长期存在的必要,也不必有复杂的组织结构。

注意范围和休息。在几天内召开两三次短会就应该能够产生一个值得尝试的设计。工作拖得太长并没什么好处。如果讨论毫无进展,可能是一次讨论的内容太多了。选一个较小的设计方面,集中讨论它。

练习使用UBIQUITOUS LANGUAGE。让其他团队成员(特别是主题专家)参与头脑风暴会议是练习和精化UBIQUITOUS LANGUAGE的好机会。这样,原来的开发人员可以得到更完善的UBIQUITOUS LANGUAGE,并反映到编码中。

成熟的头脑风暴是灵活机动、不拘泥于形式的,而且具有令人难以置信的高效率。

借鉴先前的经验

我们没有必要总去做一些无谓的重复工作。用于查找缺失概念或改进模型的头脑风暴过程具有巨大的作用,通过这个过程可以收集来自各个方面的想法,并把这些想法与已有知识结合起来。随着知识消化的不断开展,就能找到当前问题的答案。

我们可以从书籍和领域自身的其他知识源获得思路。尽管相关领域的人员可能还没有创建出适合运行软件的模型,但他们可能已经把概念很好地组织到了一起,并发现了一些有用的抽象。把这些知识结合到知识消化过程中,可以更快速地得到更丰富的结果,而且这个结果也更为领域专家们所熟悉。

有时我们可以从分析模式中汲取他人的经验。这些经验对于帮助我们读懂领域起到了一定的作用,但分析模式是专门针对软件开发的,因此应该直接根据我们自己在领域中实现软件的经验来利用这些模式。分析模式可以提供精细的模型概念,并帮助我们避免很多错误。但它们并不是现成的"菜谱"。它们只是为知识消化过程提供了一些供给。随着零散知识的归纳,必须同时处理模型关注点和设计关注点。同样,这并不意味着总是需要从头开发一切。当设计模式既符合实现需求,又符合模型概念时,通常就可以在领域层中应用这些模式。

同样,当一种常见的形式体系(如算术逻辑或谓词逻辑)与领域的某个部分非常符合时,可以把这个部分提取出来,并根据它来修改形式系统的规则。这可以产生非常简练且易于理解的模型。

针对开发人员的设计

软件不仅仅是为用户提供的,也是为开发人员提供的。开发人员必须把他们编写的代码与系统的其他部分集成到一起。在迭代过程中,开发人员反复修改代码。开发人员应该通过重构得到更深层的理解,这样既能够实现柔性设计,也能够从这样一个设计中获益。

柔性设计能够清楚地表明它的意图。这样的设计使人们很容易看出代码的运行效果,因此也很容易预计修改代码的结果。柔性设计主要通过减少依赖性和副作用来减轻人们的思考负担。这样的设计是以深层次的领域模型为基础的,在模型中,只有那些对用户最重要的部分才具有较细的粒度。在这样的模型中,那些经常需要修改的地方能够保持很高的灵活性,而其他地方则相对比较简单。

重构的时机

如果一直等到完全证明了修改的合理性之后才去修改,那么可能要等待太长时间了。项目正在承受巨大的耗支,推迟修改将使修改变得更难执行,因为要修改的代码已经变得更加复杂,并更深地嵌入到其他代码中。

持续重构渐渐被认为是一种"最佳实践",但大部分项目团队仍然对它抱有很大的戒心。人们虽然看到了修改代码会有风险,还要花费开发时间,但却不容易看到维持一个拙劣设计也有风险,而且迁就这种设计也要付出代价。想要重构的开发人员往往被要求证明其重构的合理性。虽然这看似合理,但这使得一个本来就很难进行的工作变得几乎不可能完成,而且会限制重构的进行(或者人们只能暗地里进行)。软件开发并不是一个可以完全预料到后果的过程,人们无法准确地计算出某个修改会带来哪些好处,或者是不做某个修改会付出多大代价。

在探索领域的过程中、在培训开发人员的过程中,以及在开发人员与领域专家进行思想交流的过程中,必须始终坚持把"通过重构得到更深层理解"作为这些工作的一部分。因此,当发生以下情况时,就应该进行重构了:

设计没有表达出团队对领域的最新理解;

重要的概念被隐藏在设计中了(而且你已经发现了把它们呈现出来的方法);

发现了一个能令某个重要的设计部分变得更灵活的机会。

我们虽然应该有这样一种积极的态度,但并不意味着可以随随便便做任何修改。在发布的前一天,就不要进行重构了。不要引入一些只顾炫耀技术能力而没有解决领域核心问题的"柔性设计"。无论一个"更深层的模型"看起来有多好,如果你不能说服领域专家们去使用它,那么就不要引入它。万事都不是绝对的,但如果某个重构对我们有利,那么不妨在这个方向上大胆前进。

危机就是机遇

在达尔文创立进化论后的一个多世纪中,人们一直认为标准的进化模型就是物种随着时间缓慢地改变(在一定程度上这种改变是稳定的)。突然之间,这个模型在20世纪70年代被"间断平衡"(punctuated equilibrium)模型取代了。它对原有进化论进行了扩展,认为**长期的缓慢变化或稳定变化会被相对来说很短的、爆发性的快速变化所打断。然后事物会进入一个新的平衡。**软件开发与物种进化之间的不同点是前者具有明确的方向(虽然在某些项目上可能并不明显),尽管如此软件开发仍遵循这种进化规律。

传统意义上的重构听起来是一个非常稳定的过程。但通过重构得到更深层理解往往不是这样的。在对模型进行一段时间稳定的改进后,你可能突然有所顿悟,而这会改变模型中的一切。这些突破不会每天都发生,然而很大一部分深层模型和柔性设计都来自这些突破。

这样的情况往往看起来不像是机遇,而更像危机。例如,你突然发现模型中有一些明显的缺陷,在表达方面显示出一个很大的漏洞,或存在一些没有表达清楚的关键区域。或者有些描述是完全错误的。

这些都表明团队对模型的理解已经达到了一个新的水平。他们现在站在更高的层次上发现了原有模型的弱点。他们可以从这种角度构思一个更好的模型。

通过重构得到更深层理解是一个持续不断的过程。人们发现一些隐含的概念,并把它们明确地表示出来。有些设计部分变得更具有柔性,或许还采用了声明式的风格。开发工作一下子到了突破的边缘,然后开发人员跨越这条界线,得到了一个更深层的模型,接下来又重新开始了稳步的改进过程。

参考

《领域驱动设计 软件核心复杂性应对之道》 Eric Evans 著, 赵俐 盛海艳 刘霞 等译, 任发科 审校

相关推荐
喵叔哟2 天前
重构代码之删除对参数的赋值
重构
fishjam2 天前
[开源重构]Search(Elasticsearch/OpenSearch) Sync Tool
elasticsearch·重构·开源
多多*2 天前
Vue.js 插槽 Slots 实际应用 最近重构项目的时候遇到的...
前端·vue.js·重构
老肖相当外语大佬3 天前
懂了这个道理,人月神话不再是神话!
ddd·领域驱动设计·人月神话·交付效率
喵叔哟4 天前
重构代码之内联类
服务器·重构
喵叔哟4 天前
重构代码之替换算法
重构
有趣的杰克8 天前
移动端【01】面试系统的MVVM重构实践
面试·职场和发展·重构
喵叔哟8 天前
重构代码之内联临时变量
重构
喵叔哟8 天前
重构代码之拆分临时变量
重构