几种常见的代码重构方法
来源:互联网 发布:淘宝客数据采集 编辑:程序博客网 时间:2024/05/22 08:20
Martin Fowler的著作《Refactoring: Improving the Design of Existing Code》
1.Extract Method(提取函数)
样例代码:
public void PrintOwing(double amount)
{
PrintBanner();
// print details
Console.WriteLine("name:" + _name);
Console.WriteLine("amount:" + _amount);
}
重构为:
public void PrintOwing(double amount)
{
PrintBanner();
PrintDetails(amount);
}
public void PrintDetails(double amount)
{
// print details
Console.WriteLine("name:" + _name);
Console.WriteLine("amount:" + _amount);
}
思想:让每个函数只做一件事,抽取能够被组织在一起的代码,并单独抽取为一个函数。如果函数的粒度小,被复用的机会就更大。
我常看到初学ASP.NET的同学喜欢把所有的代码全部写在Page_Load事件中。我们暂且不论分层架构,光看页面本身,如果所有的代码都放在一个函数中,那么可重用性就几乎为0。
打个比方,你的页面上有个GridView,你希望打开网页的时候给它绑定数据,于是你讲这段代码放在了Page_Load中,当 然,Page_Load还会做除此以外的很多处理。然而,在你相应页面的其他事件的时候(比如点击Button、处理用户输入等),仍然需要重新绑定 GridView的数据。这时候你就非常希望不要重写一遍数据绑定的逻辑(如果重写一个逻辑很多次,那以后要改的话就得改很多处)。于是,我们就应该把数 据绑定逻辑提取到一个单独的函数中。比如叫BindData(),然后分别在Page_Load事件,和其他你需要再次绑定数据的事件中调用 BindData()。
PS: 有时候需要重新绑定数据,单独写GridView1.DataBind();是不行的。
Visual Studio中做这样的重构很简单:选中你要提取的代码片段,然后点右键->重构->提取方法,键入一个新方法名称即可。VS会自动处理该片段与已有函数的调用关系。
关于函数的命名,我建议大家用动宾短语,阅读的时候会感觉比较自然。
2.Inline Temp(内联临时变量)
样例代码:
double basePrice = anOrder.BasePrice();
return (basePrice > 1000);
重构为:
return (anOrder.BasePrice() > 1000);
注意:重构前要保证这个临时变量只被赋值一次,所以当遇到for等循环语句时,要注意被内联的变量的值是否会在循环中被改变。有时在执行for之前定义固定的临时变量是必要的。尤其是当临时变量取自某对象的属性,而这个对象将在for循环中被更改。
3.Replace Temp with Query(以查询取代临时变量)
样例代码:
double basePrice = _quantity * _itemPrice;
if(basePrice > 1000)
{
return basePrice * 0.95;
}
else
{
return basePrice * 0.98;
}
重构为:
if(BasePrice() > 1000)
{
return BasePrice() * 0.95;
}
else
{
return BasePrice() * 0.98;
}
...
double BasePrice()
{
return _quantity * _itemPrice;
}
其实对于这个重构,我还是有些疑惑的。我们发现,BasePrice()会被计算多次,但书上说不用担心性能问题,不过我对此还是保持怀疑。毕竟有 些计算开销是很大的,并且执行多次可能产生额外的影响,所以我建议大家使用Replace Temp with Query手法的时候要谨慎,如果给一个临时变量复制的操作开销很大,尤其是需要和数据库交互,我不建议采用该手法重构。
4.Replace Nested Conditional with Guard Clauses(以卫语句嵌套条件表达式)
样例代码:
double GetPayAmount()
{
double result;
if(_isDead) result = deadAmount();
else
{
if(_isSeparated)
{
result = separatedAmount();
}
else
{
if(_isRetired)
{
result = retiredAmount();
}
else
{
result = normalPayAmount();
}
}
}
return result;
}
重构为
double GetPayAmount()
{
if(_isDead)
{
return deadAmount();
}
if(_isSeparated)
{
return separatedAmount();
}
if(_isRetired)
{
return retiredAmount();
}
return normalPayAmount();
}
这个重构手法也是我用的很多的,在初学编程的时候,很多人都会写出很多if…else嵌套的代码,其实很多时候,else并不是必须的。关于if…else…如何取决,关键看你对各分支的重视程度。
摘录书上的原话:
“根据我的经验,条件表达式通常有两种表现形式。第一种形式是:所有分支都属于正常行为。第二种形式则是:条件表达式提供的答案中只有一种是正常行 为,其他都不是常见的情况。这两类表达式有不同的用途,这一点应该通过代码表现出来。如果两条分支都是正常行为,就应该用形如if…else…的条件表达 式;如果某个条件及其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为卫语句”
插一句题外话,我平时写程序,遇到不正常情况需要提示,我通常会在if里throw一个exception,这些if是并行的,不是嵌套的,最后在catch里捕获消息并弹窗提示,我不知道这种做法是否好,求高手指点: )
5.Introduce Explaining Variable(引入解释性变量)
样例代码:
if((platform.ToUpper().IndexOf("MAC") > -1) && (browser.ToUpper().IndexOf("IE") > -1) && wasInitialized() && resize > 0)
{
// do something
}
重构为:
bool isMacOs = platform.ToUpper().IndexOf("MAC") > -1;
bool isIE = browser.ToUpper().IndexOf("IE") > -1;
bool wasResized = resize > 0;
if(isMacOs && isIE && wasInitialized() && wasResized)
{
// do something
}
这种重构手法的目的显而易见,不多叙述了。
6. 我自己常用的代码重构片段
1. 关于if和return
// 原始片段
int row = DbHelperSql.ExecuteSql(sql);
if(row) > 1
{
return true;
}
else
{
return false;
}
// 这样写可以省略一对括号
int row = DbHelperSql.ExecuteSql(sql);
if(row) > 1
{
return true;
}
return false;
// 逻辑是这样,直接return一个表达式
int row = DbHelperSql.ExecuteSql(sql);
return (row > 1);
// 应用"Replace Temp with Query"手法
return DbHelperSql.ExecuteSql(sql) > 1;
2. 三目表达式
// 如果碰到非bool类型的返回值可以这样
return Function() == 1 ? "Hehe" : "haha";
“Replace Temp with Query”中的例子也可以写成三目表达式:
return BasePrice() > 1000 ? BasePrice() * 0.95 : BasePrice() * 0.98;
- 几种常见的代码重构方法
- Nginx几种常见实现301重定向方法上的区别
- Java代码重构的几种模式
- Java代码重构的几种模式
- 代码重构的方法
- 代码重构的方法
- JS重定向的几种方法
- 重启网卡的几种方法
- List去重的几种方法
- 数组去重的几种方法
- 数组去重的几种方法
- 数组去重的几种方法
- 数组去重的几种方法
- 网页代码中最常见的几路注释方法
- ORACLE 数据去重查询的几种常见方式
- 几种常见的排序方法
- 几种常见的数据库连接方法
- 几种常见的ajax实现方法
- 线性表
- SQL 性能优化
- 心灵鸡汤【7】
- Android打开设置界面
- 二进制转换器
- 几种常见的代码重构方法
- 使用 Apache Lucene 和 Solr 进行位置感知搜索
- Linux 替换^M字符 方法
- jQuery基础知识,很赞的!!!
- 心灵鸡汤【8】
- 新工作eclipse设置
- HTML5 动画演示
- socket 编程初次接触学习
- iis7.5 http 500 内部服务器错误