对软件架构进行重构
Martin Fowler的Refactoring一书为我们列举了一系列的对代码进行重构方法。架构也是类似的。
重构到模式
Joshua Kerievsky在《Refactoring to Patterns》一书中这样描述重构和模式的关系:
Patterns are a cornerstone of object-oriented design, while test-first programming and merciless refactoring are cornerstones of evolutionary design
(模式是面向对象设计的基石,而测试优先编程和无情的重构则是设计演进的基石)。作者在文中着重强调了保持适度设计的重要性。
在作者看来,模式常常扮演着过度设计的角色。而在解决这个问题的同时又利用模式的优点的解决方法是避免在一开始使用模式,而是在设计演进中重构到模式。这种做法非常的有效,因为在初始设计中使用模式的话,你的注意力将会集中到如何使用模式上,而不是集中在如何满足需求上。这样就会导致不恰当的设计(过度设计或是设计不充分)。因此,在初始设计中,除非非常有把握(之前有类似的经验),否则我们应当把精力放在如何满足需求上。在初始模型完成后(参见精化和合并模式中的例子),我们会对架构进行重构,而随着迭代的演进,需求的演进,架构也需要演进,这时候也需要重构行为。在这些过程中,如果发现某些部分的设计需要额外的灵活性来满足需求,那么这时候就需要引入模式了。
在软件开发过程中,我们更常的是遇见设计不充分的情况,例如组件之间耦合度过高,业务层向客户端暴露了过多的方法等等。很多的时候,产生这种现象是由于不切实际的计划而导致的。开发人员不得不为了最终期限而赶工,所有的时间都花费在新功能上,而完成的软件则被仍在一边。这样产出的软件是无法保证其质量的。对于这种情况,我们也需要对设计进行重构,当然,合理的计划是大前提所在。团队的领导者必须向高层的管理者说明,现在的这种做法只会导致未来的返工,目前的高速开发是以牺牲未来的速度为代价的。因为低劣的设计需要的高成本的维护,这将抵消前期节省的成本。如果软件团队需要可持续的发展,那么请避免这种杀鸡取卵的行为。
因此,使用模式来帮助重构行为,以实现恰当的设计。
测试行为
重构的前提是测试优先,测试优先是XP中很重要的一项实践。对于编码来说,测试优先的过程是先写测试用例,再编写代码来完成通过测试用例(过程细节不只如此,请参看XP的相关书籍)。但是对于架构设计来说,测试行为是发生在设计之后的,即在设计模型完成后,产出相应的测试用例,然后再编码实现。这时候,测试用例就成为联系架构设计和编码活动的纽带。
另一方面,在设计进行重构时,相应的测试用例也由很大的可能性发生改变。此时往往会发生需要改变的测试代码超出普通代码的情况。避免这种情况一种做法是令你的设计模型的接口和实现相分离,并使测试用例针对接口,而不是实现。在精化和合并模式中,我们提到了一些模式,能够有助于稳定设计和测试用例。Martin Fowler在他的Application Facade一文中,提到使用Facade模式来分离不同的设计部分,而测试则应当针对facade来进行,其思路也是如此。
考虑一个用户转帐的用例。银行需要先对用户进行权限的审核,在审核通过之后才允许进行转帐(处于简便起见,图中忽略了对象的创建过程和调用参数):
