代码重构(英语:code refactoring)指对软件代码做一些改动以增加可读性或者简化代码结构而不影响输出结果。
软件重构需要借助重构工具完成,重构工具能够修改代码同时修改所有引用该代码的地方。在极限编程的方法学中,重构需要单元测试来支持。
重构代码
在软件工程学里,重构代码一词通常是指在不改变代码的外部行为情况下而修改源代码。在极限编程或其他敏捷方法学中,重构常常是软件开发中的一部分:开发者轮流增加新的测试和功能,并重构代码来增进代码间的清晰性和一致性。同时,自动化的单元测试保证了重构后的代码仍然能够正常运作。
重构既不修正错误,也不增加新的功能。它的目的是用于提高代码的可读性或者改变代码内部结构与设计或者删除死码,使其在将来更容易进行维护和开发。重构代码可以存在于结构层面或是语意层面,但前提是不影响代码在转换前后的功能。一种进行重构的典型案例是:一个程序在现有的逻辑结构下难以加入新的功能,因此开发人员可能会对代码进行重构,以便日后继续开发。
一个重构的小范例是修改一个变量的名称使其具有更明确的含义,例如从单个字母的“i”重构为“interestRate”(利率,图一)。一个较复杂的重构的例子是把一段if区块中的代码变为一个子程序(图二)。更复杂一点的重构是用多态性来替换if
条件式。进一步还可重新设计程序的算法。在重构中最关键的是去有意地“清理”代码,把不同的功能分开,然后对重构后的代码进行测试(任何对代码的编辑都有可能引发错误)。新的实现方法需要切合实际地改善现有设计,并且不改变原软件的功能或行为。
难点
代码重构也面临诸多的挑战。首先,对重构长远的影响需要在重构后才能进行深入研究和追踪。其次,重构某些底层业务逻辑或是数据库架构几乎是不可能的。最后,对接口造成影响的重构可能造成程序开发上的困境。例如,程序员若改变某接口中的方法名称,除非他或她要对所有客户端中链接到旧方法名的参考都加以编辑,否则就只能继续维护使用旧方法名的接口,并另外建立旧名对新名的映射。
源流
首先使用“重构”一辞于出版文献是于一篇文章:《Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems, Proceedings of the Symposium on Object Oriented Programming Emphasizing Practical Applications (SOOPPA)》,1990年9月,由William F. Opdyke与Ralph E. Johnson联名出版[1]。William Opdyke的博士论文于“重构:对象导向框架”,伊利诺大学,1992年出版[2]。“重构”术语几乎至其后确定。
重构这个术语是从数字与多项式的因式分解类比而来[3]。如,x2 − 1可以被分解为(x + 1)(x − 1),这样更好的整理并展示了式子的性质(例如式子拥有两个根+1和−1)。同样,在软件重构中,结构上的改变通常会揭示原代码中曾经“隐藏”的内部结构。
上面多项式的例子很好地展示了“重构”的思路。但是,一个多项式的不同表示形式并没有优劣之分,它们展现了多项式的不同性质。在不同情况下适合的表达形式不同,并且还会随着用户的个人习惯或理解变化。这个问题于软件开发领域亦然:个别程序员可能对某既定算法的理想结构会有不同的意见。
重构方法简单列表
下面是不完整的代码重构清单。长一点的清单可以在福勒的重构书找到。因为研究者们继续努力不懈的发明以及实现重构,完整清单可能永远都不存在。
- 封装成员变量(Encapsulate Field)—将仅限于本类使用的变量重写成私有(private)成员变量,并提供访问方法(accessor method)。这种重构方式可以将与外部调用者无关的变量隐藏起来,减少代码的耦合性,并减少意外出错的概率。
- 提取方法(Extract Method)—意思是将大段代码中的一部分提取后,构成一个新方法。这种重构可以使整段程序的结构变得更清晰,从而增加可读性。这也对函数(Function)通用。
- 类一般化(Generalize Type)—将多个类/函数共享的类型抽象出可以公用的基类(base class),然后利用多态性追加每个类/函数需要的特殊函数。这种重构可以让结构更加清晰,同时可以增加代码的可维护性。
- 函数归父(Pull Up)—或译函数上移,指的是方法从子类移动到父类。
- 函数归子(Push Down)—或译函数下移,指的是方法从父类移动到子类。
- 方法更名(Rename Method)—将方法名称以更好的表达它的用途。
重构方法 | 重构前 | 重构后 |
---|---|---|
封装成员变量 |
class SomeClass {
public int memberA;
...
}
|
class SomeClass {
private int memberA;
public int getMemberA();
public void setMemberA(int a);
...
}
|
方法提取 | void Process(MyDataSet mds)
{
// Step 1 ...
int result = 0;
if (mds.isReady)
{
int data1 = mds.param[0];
int data2 = mds.param[1];
// Preprocess...
result = data1 % data2;
}
// Step 2...
}
|
void Process(MyDataSet mds)
{
// Step 1 ...
int result = 0;
if (mds.isReady)
result = CalculateMDS(mds.param[0], mds.param[1]);
// Step 2 ...
}
int CalculateMDS(int data1, int data2)
{
// Preprocess...
return data1 % data2;
}
|
一般化类型 | class Rectangle {
private:
int w, h;
public:
double Area(){
return w*h;
}
}
class Triangle {
private:
int w, h;
public:
double Area(){
return w*h/2;
}
}
|
class Polygon {
private:
int w, h;
public:
virtual double Area() = 0;
}
class Rectangle : public Polygon {
double Area(){
return w*h;
}
}
class Triangle : public Polygon {
double Area() {
return w*h/2;
}
}
|
方法更名 | public double f(double m, double a);
|
public double calculateForce(double mass, double acceleration);
|
代码重构自动化
许多软件编辑器与集成环境支持重构自动化,又称为重构浏览器。枚举如下:
- IntelliJ IDEA(Java专用)
- Eclipse给Java用的开发包(JDK)
- NetBeans(Java专用)
- Visual Studio 2005(.NET专用)
- CodeGear Delphi
- Bicycle Repair Man (页面存档备份,存于互联网档案馆)(Python专用,可与emacs与vi共享)
- Refactoring Browser,Smalltalk专用
硬件重构
尽管代码重构一词最初只用于软件代码的重构,但近年来,硬件描述语言也出现了相应的代码重构的概念。硬件重构(hardware refactoring)是对硬件描述语言中的代码进行重构的简称。由于一般认为硬件描述语言与传统编程语言有较大的区别[4],因此硬件重构被视为与传统的代码重构不同的领域。
研究者已经提出了模拟电路硬件描述语言(如VHDL-AMS)所使用的自动化重构方法[5]。根据他们的研究,硬件重构会在保持原本代码的仿真结果的同时,增进行为模型的可理解性、可扩展性和可重用性,并使得后续自动化编译过程的逻辑综合步骤可以产生更加符合预期的结构表示。此外,Synopsys公司的Mike Keating也研究了手动进行的数字电路硬件描述语言的重构[6][7]。他所研究的目标是使复杂的硬件系统更易于理解,从而提高设计人员的生产力。
注释
参考文献
外部链接
参见
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.