随笔 - 181  文章 - 15  trackbacks - 0
<2009年5月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿(1)

随笔分类

随笔档案

My Tech blog

搜索

  •  

最新评论

阅读排行榜

评论排行榜

代码的坏味道
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 阅读(151) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理