代码重构阅读笔记一

446 阅读6分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

前言

一年的开发了,不再是刚进来的那个菜鸟了(虽然技术上仍然感觉是),也渐渐明白了重构的重要性(阅读已有代码的痛苦),最近在阅读《重构-改善既有代码的设计》这本书籍,记录下阅读笔记。

代码重构简介

重构是什么

在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。

本质上,重构就是在代码写好之后改进它的设计。

重构的两点注意

重构的目的是使软件代码更容易被理解和修改,但是要尽量保证对软件可观察的外部行为尽量没有变化。(性能优化则与之相反,性能优化通常不会改变组件的行为,只会改变其内部结构,性能优化往往使代码较难理解,但为了性能不得不做)

重构的小技巧

  • 重构尽量小步前进,运用单元测试,确保无误,降低犯错几率。

  • 重构。

  • 尽量去除临时变量,临时变量往往引发问题 -P45

  • 最好不要在另一个对象的属性基础上运用switch语句。如果不得不使用,也应该在对象自己的数据上使用,而不是在别人的数据上使用(让对象,类之间的耦合性更小)

  • 状态模式和策略模式,根据你此时的需要去认定。

重构手法

Extract Method 提取方法

Move Method 移动方法(一个类中移动到另个类中)

Replace Conditional with Polymorphism(用多态性替换条件)

Self Encapsulate Field(自我封装领域,对于赋值、取值尽量使用set、get,即使是在构造函数)

Replace Type Code with State/Strategy 用状态/策略替换类型代码

使用重构技术开发的两顶帽子

在添加新功能时,不应该修改既有代码,只管添加新功能,通过测试,衡量工作进度。

在重构时,不能添加功能,只管改进程序结果,此时不应该添加任何测试(除非发现有先前遗漏的东西),只在绝对必要(用以处理接口变化)时才修改测试。

重构原则

重构应该随时随地进行,而不是为了重构而重构。你之所以重构,是疑问你想做别的什么事,而重构可以帮助你把那些事做好。

三次法则(事不过三,三则重构)

第一次想做某件事时只管去做

第二次想做类似的事会产生反感,但无奈论如何还是可以去做。

第三次再做类似的事,你就应该重构

何时重构

添加功能时重构

重构的动力:

  • 帮助自己理解修改的代码

  • 代码的设计无法帮助自己轻松添加自己所需要的特性。重构时一个快速流畅的过程,一旦完成重构,新特性的添加就会更快速、更流畅

  1. 修补错误时重构

    调试过程中运用重构,多半是为了让代码更具可读性。用重构加深理解,帮助找出bug.

    如果收到一份错误报告,而不能一眼看出bug,这就是需要重构的信号

  2. 复审代码时重构

间接层

间接层的缺点

重构往往需要引入更多的间接层,把大对象拆成多个小对象,大函数拆成许多小函数。但你应该认清的是,间接层是一把双刃剑。每次引入一些间接层,带来的成本就是需要多管理一些东西。

间接层的价值

  • 允许逻辑共享
  • 分开解释意图和实现
  • 隔离变化
  • 封装条件逻辑

如何在重构中对待间接层:

重构一方面要引入有价值的间接层,另一方面也要移除毫无价值的“寄生间接层”。(寄生间接层:本希望能够在多个地方调用,让其表现出多态性,但是实际上只有一个地方用到)

需要重构的代码

  1. 难以阅读,难以修改。
  2. 逻辑重复,难以修改。
  3. 添加新的行为需要修改已有代码,难以修改。
  4. 带有复杂条件逻辑,难以修改。

重构目标:

  1. 容易阅读。
  2. 相同的逻辑只出现一次。
  3. 新的改动不会危及现有行为。
  4. 尽可能简单地表达条件逻辑。

重构的难题

数据库-采用分隔层解决应用程序和背后的数据库结构的紧密耦合问题

修改接口-如何修改已发布的接口

在修改函数名称时,如果函数所有调用者都在开发者的控制下,那么及时修改函数名称不会有任何问题。即使是public函数。

但是,当接口被''找不到,找到也不能修改''的代码使用时,接口的修改就成为了问题,这些接口称之为 已发布接口(publish interface)

已发布接口(比如android 系统层的jar包;公司定制系统的一些接口;多模块开发中,你无法操作其他人代码,但是他人已经调用了你发布的接口)

  • 一般解决方法(保留旧接口):

    让旧接口调用新接口。并将旧接口标记为 deprecated

  • 如非必要,不要发布公共接口

修改接口-增加异常

如果在Java的方法,在throw字句中增加一个异常,如果用户代码不做出相应习惯,编译器就不会让他通过。

要解决这个问题:

一般可以为整个包定义一个异常基类,就像java.sql的SQLException,且确保所有public函数只在自己的throws子句中声明这个异常。如此之后,可以在包中所有所欲定义子类,而不会影响调用者,调用者永远只知道那个更具一般性的异常基类。

难以通过重构手法完成的设计改动

何时不该重构

  1. 当代码实在太混乱时,重构塔不如重写一个来的简单,就应该放弃重构
  2. 当项目已近最后期限,应该避免重构

重构与设计

重构和设计是彼此互补的。

使用重构,在设计时,仍旧需要思考潜在的变化,仍旧需要考虑灵活的解决放啊,但是不必逐一实现这些方案。只要你认为要把当前简单的方案重构为一个复杂的方案很简单,你就只需要实现目前的简单方案。

重构与性能

除性能有严格要求的实时系统,其他情况下 编写快速软件的秘诀:首先写出可调试的软件,再调整它以获得速度。