代码的坏味道
1、重复的代码(duplicated code) 最单纯的重复代码就是,同一个class内的两个函数含有相同的表达式。此时需要使用Extract Method,提炼出重复的代码,然后让这两个地方都调用被提炼出来的那一段代码。
另一种情况就是,两个兄弟子类中含有相同的表达式。在这种情况下,只要对两个子类都使用Extract Method,然后再对提炼出来的代码使用pull up method将它推入超类中。如果代码之间只是类似,并非完全相同,那么就使用Extract Method把相似部分和差异部分分割开,构成单独一个函数,然后就能够使用form template method获得一个template method设计模式.
下面跳到后面去看一下template method:
将各个措施放到独立的函数中,并保持它们具有相同的签名式,原函数也就变得相同了。然后将原函数上移到superclass。
下面是仿照书中写的例子。首先是Site类
public abstract class Site
{
public const float TAX_RATE=(float)0.5;
public abstract double GetBillAbleAmount();
}
现在有两个类LifelineSite和ResidentialSite分别继承自Site类并实现了方法GetBillAbleAmount
LifelineSite:
public class LifelineSite:Site
{
private float _unit;
private float _rate;
public LifelineSite(float unit,float rate)
{
//
// TODO: 在此处添加构造函数逻辑
//
this._unit=unit;
this._rate=rate;
}
public override double GetBillAbleAmount()
{
double dbBase=_unit*_rate*0.5;
double tax=dbBase*Site.TAX_RATE*0.2;
return dbBase+tax;
}
}
ResidentialSite:
public class ResidentialSite:Site
{
private float _unit;
private float _rate;
public ResidentialSite(float unit,float rate)
{
//
// TODO: 在此处添加构造函数逻辑
//
this._unit=unit;
this._rate=rate;
}
public override double GetBillAbleAmount()
{
double dbBase=_unit*_rate;
double tax=dbBase*Site.TAX_RATE;
return dbBase+tax;
}
}
现在使用template method进行重构:
首先重构类Site:
public abstract class Site
{
public const double TAX_RATE=0.5;
public abstract double GetBaseAmount();
public virtual double GetTaxAmount()
{
return GetBaseAmount()*TAX_RATE;
}
public virtual double GetBillAbleAmount()
{
return GetBaseAmount()+GetTaxAmount();
}
}
然后重构ResidentialSite:
public class ResidentialSite:Site
{
private double _unit;
private double _rate;
public ResidentialSite(double unit,double rate)
{
//
// TODO: 在此处添加构造函数逻辑
//
this._unit=unit;
this._rate=rate;
}
// public override double GetBillAbleAmount()
// {
//
// double dbBase=_unit*_rate;
// double tax=dbBase*Site.TAX_RATE;
// return dbBase+tax;
// }
public override double GetBaseAmount()
{
return _unit*_rate;
}
最后重构LifelineSite:
public class LifelineSite:Site
{
private double _unit;
private double _rate;
public LifelineSite(double unit,double rate)
{
//
// TODO: 在此处添加构造函数逻辑
//
this._unit=unit;
this._rate=rate;
}
// public override double GetBillAbleAmount()
// {
//
// double dbBase=_unit*_rate*0.5;
// double tax=dbBase*Site.TAX_RATE*0.2;
// return dbBase+tax;
// }
public override double GetBaseAmount()
{
return _unit*_rate*0.5;
}
public override double GetTaxAmount()
{
return base.GetTaxAmount()*0.2;
}
}
可以看到,通过重构首先分离了计算BaseAmount和计算TaxAmount的两个方法,然后把加和运算移动到超类Site中。GetTaxAmount由于代码中存在近似的地方,将更加通用一些的步骤移动到超类中,在子类中的重复部分调用超类中的方法,然后子类各自对超类方法进行一些修饰来达到自己的运算目的。
2、Long Method(过长函数)“间接层”所能带来的全部利益--解释能力、共享能力、选择能力--都是由小型函数支持的。
让small method容易理解的真正关键在于一个
好名字。如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。
最终的效果是:你应该更积极进取的分解函数,我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立的函数中,并以其用途(而非实现手法)命名。我们可以对一组或者甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数本身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离。
百分之九十的场合里,要把函数变小,只需要Extract Method。找到函数中适合集中在一起的部分,将它们提炼出来形成一个新函数。
如果函数内有大量的参数和临时变量,他们会对你的函数提炼形成阻碍。如果你尝试用Extract Method,最终就会把许多这些参数和临时变量当作参数,传递给被提炼出来的新函数,导致可读性几乎没有任何提升。可以消除临时元素的方法有:
Replace Temp with Query;
Introduce Parameter Object;
Preserve Whole Objet;
确定该提炼哪一段代码的很好的技巧就是:寻找注释。他们通常是指出“代码用途和实现手法间的语义距离”的信号。如果代码前方有一行注释,就是在提醒你:可以将这段代码替换成一个函数。而且可以在注释基础上给这个函数命名。条件式循环也常常是提炼的信号。你可以使用Decompose Conditional处理条件式。至于循环,你应该将循环和其内的代码提炼到一个独立函数中。
posted on 2007-06-26 23:32
littlegai 阅读(148)
评论(0) 编辑 收藏 引用