12.1 函数上移(Pull Up Method)
反向重构:函数下移(Push Down Method)
避免重复代码是很重要的。
重构前:
class Employee
{
}
class Salesman : Employee
{
public void Rest() { }
}
class Engineer : Employee
{
public void Rest() { }
}
反向重构:函数下移(Push Down Method)
避免重复代码是很重要的。
重构前:
class Employee
{
}
class Salesman : Employee
{
public void Rest() { }
}
class Engineer : Employee
{
public void Rest() { }
}
如果某个函数只提供一个值,没有任何看得到的副作用,那么这个函数可以任意调用,也很容易测试。
重构前:
decimal GetTotalOutstandingAndSendBill()
{
var result = _customer.Invoices.Sum(m => m.Amount);
SendBill();
return result;
}
第 5 章 是介绍之后几章重构手法的说明,就不单独写一篇博客了。
从第 6 章开始直到最后都是在介绍各种重构手法的。每个重构手法包含 名称、速写(Skeetch)、动机(motivation)、做法(mechanics)、范例 (examples) 5 个部分。这里只记录一下 速写(Skeetch) ,速写是用来帮助回忆重构手法的,具体的重构用途和重构的具体步骤这里就不介绍了,还是推荐大家买实体书来看。
第 4 章主要介绍了测试的价值以及一些测试实践方法。书中是以 JavaScript 为例的,.NET 开发人员可以参考 MSDN 上关于单元测试的文章。
本手法其实只是提炼函数(Extract Function)的一个应用场景。
重构前:
if (aDate >= plan.SummerStart && aDate <= plan.SummerEnd)
{
charge = quantity * plan.SummerRate;
}
else
{
charge = quantity * plan.RegularRate + plan.RegularServiceCharge;
}
曾用名:移除对参数的赋值(Remove Assignments to Parameters)
曾用名:分解临时变量(Split Temp)
如果变量承担多个职责,它就应该被替换(分解)为多个变量,每个变量只承担一个责任。
重构前:
曾用名:搬移函数(Move Method)
重构前:
class Account
{
decimal OverdraftCharge { get; }
}
曾用名:以数据类取代记录(Replace Record with Data Class)
重构前:
(string Name, string Country) organization = ("JiaJia's Blog", "China");
第 3 章介绍了何时应当重构?作者总结了一些常见的场景,但由于不可能给出一个精确的衡量标准,所以还需依赖开发者的经验来判断。
书中还给出了每种场景应该使用何种重构方法来解决,有兴趣的小伙伴建议买实体书来阅读。
整洁代码最重要的一环就是好的名字,所以我们会深思熟虑如何给函数、模块、变量和类命名,使它们能清晰地表明自己的功能和用法。
命名是编程中最困难的两件事之一。
为一个恼人的名字所付出的纠结,常常能推动我们对代码进行精简。
第 2 章介绍了重构的一些重大原则,这里仍然只是记录一些重点以作备忘。
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
如果有人说他们的代码在重构过程中有一两天时间不可用,基本上可以确定,他们在做的事不是重构。
重构和性能优化的差别:重构是为了让代码“更容易理解,更易于修改”;性能优化只关心让程序运行的更快。
两顶帽子:添加新功能时,不应该修改既有代码,只管添加新功能;重构时不能再添加功能,只管调整代码结构。
代码结构的流失有累积效应。
为何重构
“设计耐久性假说”:通过投入精力改善内部设计,增加了软件的耐久性,从而可以更长时间地保持开发速度。
三次法则:第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,你就应该重构。事不过三,三则重构。
重构的最佳时机就在添加新功能之前。
并不需要专门安排一段时间来重构,而是在添加新功能或修改 bug 的同时顺便重构。
肮脏的代码必须重构,但漂亮的代码也需要很多重构。
添加新功能最快的方法往往是修改现有的代码,使新功能容易被加入。
重构的唯一目的就是让我们开发更快,用更少的工作量创造更大的价值。
重构应该总是由经济利益驱动。
已发布接口(published interface):接口的使用者(客户端)与声明者彼此独立,声明者无权修改使用的代码。
在隔离的分支上工作的越久,将完成的工作集成(integrate)回主线就会越困难。
持续集成(CI),也叫“基于主干开发”(Trunk-Based Development)。使用 CI 时,每个团队成员每天至少向主线集成一次。代价:必须使用相关的实践以确保主线随时处于健康状态。
CI 和重构能良好的配合。
团队必须投入时间和精力在测试上,但收益绝对是划算的。
缺乏测试时的重构
如果我的开发环境很好的支持自动化重构,就可以信任这些重构,不必运行测试。
现在工作的主要开发工具是 Visual Studio 和 Intelli IDEA,对重构都有很好的支持,很多简单的重构都可以自动化的完成。
只使用一组经过验证是安全的重构手法。
一般来说,只有在设计系统时就考虑到了测试,这样的系统才容易添加测试。
这个说到痛点上了,最近搭建的 .NET Core 项目的时候也没有考虑这方面的需求,很多功能也是基于之前的框架结构,想添加测试总感觉无从下手。
数据库:渐进式数据库设计和数据库重构:借助数据迁移脚本,将数据库结构的修改与代码结合,使大规模的、涉及数据库的修改可以比较容易的展开。
重构起初是作为极限编程(XP)的一部分被人们采用的。
重构的第一块基石是自测试代码。
最近在看 马丁·福勒(Martin Fowler)写的《重构:改善既有代码的设计》(《Refactoring: Improving the Design of Existing Code》)第二版。
第一版应该是 1999 年出版的,第二版是 2018 年底出版的。示例的语言从 Java 换成了 JavaScript ,但其说明的重构方法适用于所有的开发语言。