何谓重构
重构的两种定义,一种名词形式,一种是动词形式:
重构(名词):对软件内部结构的一种调整,目的是在不改变软件之可察行为前提下,提高其可理解性,降低其修改成本。
重构(动词):使用一系列重构准则(手法),在不改变软件之可察行为前提下,调整其结构。
重构不仅仅是整理代码,它提供了一种更高效且受控的代码整理技术。
两顶帽子:‘添加新功能’和‘重构’,在软件开发过程中,你可能会发现自己经常变换帽子,但无论何时你都应该清楚自己戴的是哪一顶帽子。
我的理解:重构是种约束性的软件结构调整行为,它有一系列准则,其约束性是不改变软件之可察行为。
为何重构?
重构改进软件设计;
- 可以改善腐败变质的程序,你所作的就是让所有东西回到应该的位置上;
- 消除重复代码,你可以确定代码将所有事物和行为都只表述一次,唯一一次,这正是优秀设计的根本。
重构使软件更易被理解;
- 编程模式的核心就是“准确说出吾人所欲”。
重构助你找到臭虫;
- 搞清楚程序结构的同时,也清楚了所做的一些假设,从这个角度说,不找到臭虫都难矣。
重构助你提高编程速度;
- 重构可提高软件质量(改善设计、提升可读性、减少错误),良好设计是快速软件开发的根本。
我的理解:这四点重构的原因,均可归结到重构可维持良好设计这一点上,重构是方法准则,良好设计是重构的结果,这四点是良好设计需具备的条件。
何时重构
重构本来就不是一件特别拨出时间做的事情,重构应该随时随地进行。
三次法则:
- 添加功能时一并重构;
- 修补错误时一并重构;
- 复审代码时一并重构;
为什么重构有用?
程序有两面价值:‘今天可以为你做什么’和‘明天可以为你做什么’。
对于今天的工作,我了解的很充分;对于明天的工作,我了解的不够充分。但如果我纯粹只是为今天的工作,明天我将完全无法工作。
程序为什么如此难与的四个原因:
- 难以阅读的程序,难以修改;
- 逻辑重复的程序,难以修改;
- 添加新行为时需要修改既有代码者,难以修改;
- 带复杂条件逻辑的程序,难以修改;
因此,我们希望程序:
- 容易阅读;
- 所有逻辑都只在唯一地点制定;
- 新的改动不会危及现有行为;
- 尽可能简单表达条件逻辑。
重构是这样一个过程:它在一个目前运行的程序上进行,企图在不改变程序行为的情况下赋予上述美好性质,是我们能够继续保持告诉开发。
怎么对经理说?
经理的角度:经常嘴巴上说自己是‘质量驱动’,其实更多的是‘进度驱动’。对于这种情况,作者则给了一个较有争议的建议:不要告诉经理。
我的理解:如果满足2.3的三次法则,重构行为随时随地进行,重构习惯融入到程序开发、修改、复审环节中,似乎重构与否的问题没有理由让经理来面对,但是那只是理想状态,我们有时不得不单独拿出时间来进行重构工作,所以这时候就做你认为该做的。
重构的难题
数据库程序难以重构的两个方面:
- 程序与它们背后的数据表格结构紧密耦合,难以修改;
- 数据迁移。
解决方法程序与数据表耦合的方案:在对象模型和数据库模型之间插入一个分隔层,这就可以隔离两个模型各自的变化,升级某一个模型时无需同时升级另一模型,只需升级上述的分隔层。这样分隔层会增加系统复杂度,但可以给你很大的灵活度。无需一开始就插入分隔层,可以在发现对象模型变得不稳定时再产生它。
解决数据迁移的方案:
- 对象数据库提供不同版本的对象之间的自动迁移功能;
- 无自动功能,在数据尚未被转移钱先运用访问函数造成数据已转移的假象,一旦确定知道数据应该在何处时,就可以一次性地将数据迁移过去,这时唯一需要修改的只有访问函数。
重构了修改了已发布接口的解决方案:
- 让就接口调用新接口,当你要修改某个函数名称时,请留下就函数,让他调用新函数。千万不要拷贝函数实现码,那会让你陷入重复代码的泥沼中难以自拔;
- 除非真有必要,不要发布接口,团队内改变代码拥有权观念,让每个人都可以修改别人的代码。
总结:不要过早发布接口。请修改你的代码拥有全政策,使重构更顺畅。
何时不该重构?
- 既有代码是在太混乱,重构它还不如重新写一个来得简单;
- 现有代码根本不能正常运作;
- 如果项目已近最后期限,你也应该避免重构。
文中经典比喻:把重构工作比做成债务,把过于复杂的代码造成的‘维护和扩展的额外开销’比作成要付的利息,你可以承受一定程度的利息,但如果利息太高你就会被压垮,你应该随时通过重构来偿还一部分债务。
重构与设计
重构可以成为预先设计的替代品,极限编程的支持者提倡这种方法,但只运用重构也能收到效果,但不是最有效途径,xp爱好者也会进行预先设计,使用crc卡类似的东西检验想法,然后得到一个可被接受的方案,然后开始编码,然后才能重构。
重构改变了预先设计的角色,不必保证预先设计的准确无误,只需要得到一个足够合理的解决方案就够了。
重构与性能
编写快速软件的秘密就是首先写出可调软件,然后调整它以获得足够速度。
编写快速软件的三种方法:
- 时间预算法——这通常只用于性能要求极高的实时系统。如果使用这种方法,分解你的设计时就要做好预算,给每个组件预先分配一定资源,包括时间和执行轨迹。每个组件绝对不能超出自己的预算,就算拥有可在不同组件之间调度预配时间的机制也不行;
- 持续关切法——这种方法要求程序员在任何时间做任何事时,都要设法保持系统的高性能;
- 90%统计数据——以一种良好的分解方式来建造自己的程序,不对性能投以任何关切,直至进入性能优化阶段,进入该阶段再按照某个特定程序来调整程序性能。
性能优化阶段的优化过程:首先应该以一个量测工具监控程序的运行,由它得知程序大量消耗时间和空间的位置,这样可以找出性能热点(hot spot)所在的一小段代码,对该性能热点,使用持续关切法中的优化手段。应该小幅度进行修改,每走一步都需要编译、测试、再次量测。如果没有调高性能,你应该继续这个“发现热点、去除热点”的过程。
一个被良好分解的程序可从两方面帮助此种优化形式:
- 它让你有比较充裕的时间进行性能调整;
- 它让你在进行性能分析时便有较细的粒度。