一个软件总是为解决某种特定的需求而产生,时代在发展,客户的业务也在发生变化。有的需求相对稳定一些,有的需求变化的比较剧烈,还有的需求已经消失了,或者转化成了别的需求。在这种情况下,软件必须相应的改变。
考虑到成本和时间等因素,当然不是所有的需求变化都要在软件系统中实现。但是总的说来,软件要适应需求的变化,以保持自己的生命力。
这就产生了一种糟糕的现象:软件产品最初制造出来,是经过精心的设计,具有良好架构的。但是随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能,还免不了有一些缺陷需要修改。为了实现变更,不可避免的要违反最初的设计构架。经过一段时间以后,软件的架构就千疮百孔了。bug越来越多,越来越难维护,新的需求越来越难实现,软件的架构对新的需求渐渐的失去支持能力,而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本,这就是这个软件系统的生命走到尽头的时候。
重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后,使用重构的方式,不改变系统的外部功能,只对内部的结构进行重新的整理。通过重构,不断的调整系统的结构,使系统对于需求的变更始终具有较强的适应能力。
重构可以降低项目的耦合度,使项目更加模块化,有利于项目的开发效率和后期的维护。让项目主框架突出鲜明,给人一种思路清晰,一目了然的感觉,其实重构是对框架的一种维护。
那么说到重构,可能我们普遍认为就是改改框架、使用一些最新流行的框架或者技术等等,我们到底要从哪些方面来对项目进行重构呢?下面我们进入主题:控制圈复杂度的9种重构技术
- 提炼函数
例子:如果某段代码可以被组织在一起并独立出来
void printOwing(double previousAmount)
{
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
// 打印大标题
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
// 计算未完成的订单数量
while (e.hasMoreElements())
{
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}
//打印明细
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
将这段代码放进一个独立函数中,并让函数名称解释该函数的用途
void printOwing(double previousAmount)
{
printBanner();
double outstanding = getOutstanding(previousAmount * 1.2);
printDetails(outstanding);
}
void printBanner() //打印大标题
{
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
}
// 计算未完成的订单数量
double getOutstanding(double initialValue)
{
double result = initialValue;
Enumeration e = _orders.elements();
while (e.hasMoreElements())
{
Order each = (Order) e.nextElement();
result += each.getAmount();
}
return result;
}
void printDetails (double outstanding) //打印明细
{
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
- Substitute Algorithm(替换你的算法)
例子:
把当前算法重构成更清晰的算法
String foundPerson(String[] people)
{
for (int i = 0; i < people.length; i++)
{
if (people[i].equals ("Don"))
return "Don";
if (people[i].equals ("John"))
return "John";
if (people[i].equals ("Kent"))
return "Kent";
}
return "";
}
重构成更清晰的算法
String foundPerson(String[] people)
{
List candidates = Arrays.asList(new
String[]{"Don", "John","Kent"});
for (int i=0; i<people.length; i++)
if (candidates.contains(people[i]))
return people[i];
return "";
}
- Decompose Conditional(分解条件式)
例子:你有一个复杂的条件语句
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else
charge = quantity * _summerRate;
从if、then、else三个段落中分别提炼出独立函数
if (notSummer(date))
charge = winterCharge(quantity);
else
charge = summerCharge (quantity);
- Consolidate Conditional Expression(合并条件式)
例子:你有一系列条件判断,都得到相同结果
double disabilityAmount()
{
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
将这些判断合并为一个条件式,并将这个条件式提炼成为一个独立函数,函数名自注释
double disabilityAmount()
{
if (isNotEligableForDisability()) return 0;
// compute the disability amount
- Consolidate Duplicate Conditional Fragments(合并重复的条件片断)
例子:在条件式的每个分支上有着相同的一段代码。
if (isSpecialDeal())
{
total = price * 0.95;
send();
}
else
{
total = price * 0.98;
send();
}
将这段重复代码搬移到条件式之外,避免用拷贝粘贴的方式写重复代码
if (isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();
- Remove Control Flag(移除控制标记)
例子:当前代码使用标记变量,可读性差,容易出错
void checkSecurity(String[] people) {
boolean found = false;
for (int i = 0; i < people.length; i++) {
if (! found) {
if (people[i].equals ("Don")){
sendAlert();
found = true;
}
if (people[i].equals ("John")){
sendAlert();
found = true;
}
}
}
}
以break和return取代标记变量
void checkSecurity(String[] people) {
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
sendAlert();
break;
}
if (people[i].equals ("John")){
sendAlert();
break;
}
}
}
- Separate Query from Modifier(将查询函数和修改函数分离)
例子:某个函数既返回对象状态值,又修改对象状态,建立两个不同的函数,其中一个负责查询,另一个负责修改
- Parameterize Method(令函数携带参数)
例子:若干函数做了类似的工作,但在函数本体中却
包含了不同的值
Dollars baseCharge()
{
double result = Math.min(lastUsage(),100) * 0.03;
if (lastUsage() > 100) {
result += (Math.min (lastUsage(),200) - 100) * 0.05;
};
if (lastUsage() > 200) {
result += (lastUsage() - 200) * 0.07;
};
return new Dollars (result);
}
建立单一函数,以参数表达那些不同的值
Dollars baseCharge()
{
double result = usageInRange(0, 100) * 0.03;
result += usageInRange (100,200) * 0.05;
result += usageInRange (200, Integer.MAX_VALUE) * 0.07;
return new Dollars (result);
}
int usageInRange(int start, int end)
{
if (lastUsage() > start)
return Math.min(lastUsage(),end) -start;
else
return 0;
}