重构啊重构
重构之途确实坎坷万分,稍微不注意就落得鸡飞狗跳之田地。
【起因】
原来的程序基本上是“面向数据结构”而不是“面向对象”:所有的业务数据都放
在STL的vector/map/list这样的全局数据结构里面,所有针对业务数据的操作都直
接操作vector/map/list的元素,封装可谓差极,仅比传说中的“面向过程”的编程
方式稍好而已。
【目标】
于是我决定对其进行全面整改:将所有暴露在外的全局数据结构藏进业务数据“对
象”中,将所有针对该结构的业务操作封装为对象的方法。说到底就是从数据操作
中抽离业务过程,使之成为通俗易懂的业务操作原子。
【痛苦】
新的对象和公用方法瞬间就写好了,这个没什么好说的,剩下的工作是用新的对象
层次替换原有的数据结构们。
痛苦随之而来。
原本我想通过釜底抽薪之法:先注释掉原有的数据结构定义,然后编译,然后从编译
错误列表中查找调用该数据结构的代码,一处处地修改。
然而实践证明该法完全失效,皆因原来的数据结构几乎是全局共享,各个模块与其的
耦合性之紧难以想象,稍微改动一下,编译就会涌出万千错误,而且每一个错误都
迅速深入业务底层细节,使动手之人完全被细节的海洋淹没,哪里还有心情完成修改。
幸好我遵从重构的基本操作守则--充分的测试并随时roll back,所以可以迅速回退
到灾难发生之前的正常状态中。经过数次折磨之后,总算找到一条可行之途,使得重
构的未来不是梦。
【解决】
其实只要不一下子那么狠,把人家釜底的柴全部抽走就行。学会慢慢地抽,从上面的
小柴抽起,让火焰慢慢变小,直至无声无息地熄灭,那么就基本上不会引发无法收拾
的严重后果了。
这种方法我称为和平演变。
首先是找到全局结构的“主人”--也即往全局结构中填充数据的管理者,让它们向旧
结构填充数据的同时也向新的对象填充数据。做每一步之后记得重新编译和测试。如果
没有意外,基本上这一步是不会出什么漏子的。
然后是找到那些全局结构的“客户”--也即访问者,让它们转用新的对象。这个过程
可能颇长,但是因为每一个客户都是基本独立的,所以改完一个客户就可以测试一下,
确保程序在新的对象的支持下仍然能正常运行。由于新的对象中已经有数据,所以这一步
的成功很关键,只要能完成这一步,基本上就能使所有的客户逐渐摆脱对旧结构的依赖。
最后当然是釜底抽薪了--直接删掉旧的结构的定义头文件,然后编译,然后逐个修改
编译错误即可。基本上经过了上面两步,这一步要做的,只是删掉多余的#include而已。
重构至此完成。
【推广】
我的工作环境是C++,实际上在C#中毫无二致。多少号称使用“面向对象编程”的程序员们,
仅仅是定义了一堆public的Hashtable, ArrayList在全局类中,然后在业务操作中直接
修改这些“对象”。(哈,我把这种手法称为“面向数据结构的编程”,信口定义,大家莫
要作准。)
所以C#中的重构手法其实也是一样的。
eXcel Wong
Last Update: 12:27:12 Friday, June 17, 2005
【起因】
原来的程序基本上是“面向数据结构”而不是“面向对象”:所有的业务数据都放
在STL的vector/map/list这样的全局数据结构里面,所有针对业务数据的操作都直
接操作vector/map/list的元素,封装可谓差极,仅比传说中的“面向过程”的编程
方式稍好而已。
【目标】
于是我决定对其进行全面整改:将所有暴露在外的全局数据结构藏进业务数据“对
象”中,将所有针对该结构的业务操作封装为对象的方法。说到底就是从数据操作
中抽离业务过程,使之成为通俗易懂的业务操作原子。
【痛苦】
新的对象和公用方法瞬间就写好了,这个没什么好说的,剩下的工作是用新的对象
层次替换原有的数据结构们。
痛苦随之而来。
原本我想通过釜底抽薪之法:先注释掉原有的数据结构定义,然后编译,然后从编译
错误列表中查找调用该数据结构的代码,一处处地修改。
然而实践证明该法完全失效,皆因原来的数据结构几乎是全局共享,各个模块与其的
耦合性之紧难以想象,稍微改动一下,编译就会涌出万千错误,而且每一个错误都
迅速深入业务底层细节,使动手之人完全被细节的海洋淹没,哪里还有心情完成修改。
幸好我遵从重构的基本操作守则--充分的测试并随时roll back,所以可以迅速回退
到灾难发生之前的正常状态中。经过数次折磨之后,总算找到一条可行之途,使得重
构的未来不是梦。
【解决】
其实只要不一下子那么狠,把人家釜底的柴全部抽走就行。学会慢慢地抽,从上面的
小柴抽起,让火焰慢慢变小,直至无声无息地熄灭,那么就基本上不会引发无法收拾
的严重后果了。
这种方法我称为和平演变。
首先是找到全局结构的“主人”--也即往全局结构中填充数据的管理者,让它们向旧
结构填充数据的同时也向新的对象填充数据。做每一步之后记得重新编译和测试。如果
没有意外,基本上这一步是不会出什么漏子的。
然后是找到那些全局结构的“客户”--也即访问者,让它们转用新的对象。这个过程
可能颇长,但是因为每一个客户都是基本独立的,所以改完一个客户就可以测试一下,
确保程序在新的对象的支持下仍然能正常运行。由于新的对象中已经有数据,所以这一步
的成功很关键,只要能完成这一步,基本上就能使所有的客户逐渐摆脱对旧结构的依赖。
最后当然是釜底抽薪了--直接删掉旧的结构的定义头文件,然后编译,然后逐个修改
编译错误即可。基本上经过了上面两步,这一步要做的,只是删掉多余的#include而已。
重构至此完成。
【推广】
我的工作环境是C++,实际上在C#中毫无二致。多少号称使用“面向对象编程”的程序员们,
仅仅是定义了一堆public的Hashtable, ArrayList在全局类中,然后在业务操作中直接
修改这些“对象”。(哈,我把这种手法称为“面向数据结构的编程”,信口定义,大家莫
要作准。)
所以C#中的重构手法其实也是一样的。
eXcel Wong
Last Update: 12:27:12 Friday, June 17, 2005