返回总目录html
本小节目录算法
你有一个大型函数,其中对局部变量的使用使你没法采用Extract Method。this
将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段。而后你能够在同一个对象中将这个大型函数分解为多个小型函数。spa
咱们一直在强调,小型函数优美动人。只要将相对独立的代码从大型函数中提炼出来,就大大提升了函数的可读性。code
可是,局部变量的存在会增长函数分解难度。若是一个函数中局部变量泛滥成灾,那么这个时候Replace Temp with Query能够帮助你。有时候根本没法拆解一个须要拆解的函数,这时候Replace Method with Method Object就发挥做用了。htm
Replace Method with Method Object会将全部局部变量都变成函数对象的字段。而后就能够对这个新函数使用Extract Method创造新函数,从而达到拆解的目的。
对象
若是要找到合适的例子,那么须要很长的篇幅,因此咱们杜撰了这样一个函数。blog
class Account { int Gamma(int inputVal, int quantity, int yearToDate) { int importantValue1 = inputVal * quantity + Delta(); int importantValue2 = inputVal * yearToDate + 100; if (yearToDate - importantValue1 > 100) { importantValue2 -= 20; } int importantValue3 = importantValue2 * 7; //and so on... return importantValue3 - 2 * importantValue1; } public int Delta() { return 100; } }
为了把这个函数变成函数对象,首先声明一个新类。在新类中,提供一个字段用于保存原对象,同时也对函数的每一个参数和每一个临时变量,提供字段用于保存。get
class Gamma { private readonly Account _account; private readonly int _inputVal; private readonly int _quantity; private readonly int _yearToDate; private int _importantValue1; private int _importantValue2; private int _importantValue3; }
接下来,加入一个构造函数:
public Gamma(Account account, int inputVal, int quantity, int yearToDate) { _account = account; _inputVal = inputVal; _quantity = quantity; _yearToDate = yearToDate; }
接下来,将本来的函数搬到Compute()中。
public int Compute() { _importantValue1 = _inputVal * _quantity + _account.Delta(); _importantValue2 = _inputVal * _yearToDate + 100; if (_yearToDate - _importantValue1 > 100) { _importantValue2 -= 20; } _importantValue3 = _importantValue2 * 7; //and so on... return _importantValue3 - 2 * _importantValue1; }
完整的Gamma函数以下:
class Gamma { private readonly Account _account; private readonly int _inputVal; private readonly int _quantity; private readonly int _yearToDate; private int _importantValue1; private int _importantValue2; private int _importantValue3;
public Gamma(Account account, int inputVal, int quantity, int yearToDate) { _account = account; _inputVal = inputVal; _quantity = quantity; _yearToDate = yearToDate; } public int Compute() { _importantValue1 = _inputVal * _quantity + _account.Delta(); _importantValue2 = _inputVal * _yearToDate + 100; if (_yearToDate - _importantValue1 > 100) { _importantValue2 -= 20; } _importantValue3 = _importantValue2 * 7; //and so on... return _importantValue3 - 2 * _importantValue1; } }
最后,修改旧函数,让它的工做委托给刚完成的这个函数对象。
int Gamma(int inputVal, int quantity, int yearToDate) { return new Gamma(this, inputVal, quantity, yearToDate).Compute(); }
这就是本项重构的基本原则。它的好处是:如今咱们能够轻松地对Compute()函数采起Extract Method,没必要担忧参数传递的问题。
好比说咱们对Compute进行以下重构:
public int Compute() { _importantValue1 = _inputVal * _quantity + _account.Delta(); _importantValue2 = _inputVal * _yearToDate + 100; GetImportantThing(); _importantValue3 = _importantValue2 * 7; //and so on... return _importantValue3 - 2 * _importantValue1; } void GetImportantThing() { if (_yearToDate - _importantValue1 > 100) { _importantValue2 -= 20; } }
这种重构手法是针对大型函数,并且里面的局部变量又有不少,那么这个重构方法或许会让你豁然开朗。
你想要把某个算法替换为另一个更清晰的算法。
将函数本体替换为另外一个算法。
我敢打赌,你从小到大,确定作过这样的题目:请使用两种以上的解法来回答这道问题。这就是说明,某一道题确定不仅有一种解法,并且某些方法确定会比另外一些更简单。算法也是如此。
若是你发现作一件事能够有更清晰的方式,就应该以较清晰的方式取代较复杂的方式。随着对问题有了更深刻的了解,你每每会发现,在原先的作法以外,有更简单的解决方案,此时,你要作的就是改变原先的算法。
咱们以一个简单的函数为例:
string FoundPerson(string[] people) { foreach (var person in people) { if (person == "Don") { return "Don"; } if (person == "John") { return "John"; } if (person == "Kent") { return "Kent"; } } return string.Empty; }
经过对这个算法进行分析,咱们发现此时若是用一个list集合,则函数会更简单,更清晰。因此,重构以后,代码以下:
string FoundPerson(string[] people) { var candidates = new List<string>() { "Don", "John", "Kent" }; foreach (var person in people) { if (candidates.Contains(person)) { return person; } } return string.Empty; }
使用这项重构手法以前,请先肯定本身已经尽量分解了原先函数。替换一个巨大而复杂的算法是很是困难的,只有先将它分解为较简单的小型函数,而后你才能颇有把握地进行算法替换工做。
这几小节的大标题叫作从新组织函数,顾名思义这些重构手法都是针对函数进行整理,使之更恰当地包装代码。几乎全部时刻,问题都源于代码的坏味道之Long Method(过长函数)。
对于过长函数,一项重要的重构手法就是Extract Method,它把一段代码从原先函数中提取出来,放进独立的函数中。而Inline Method则正好相反,它将一个函数调用替换为该函数本体。
提炼函数时最大的困难就是处理局部变量,其中一个即是临时变量。处理一个函数时,能够先运用Replace Temp with Query去掉全部可能的临时变量。若是多个地方使用了某个临时变量,请先运用Split Temporary Variable将它变得比较容易替换。
若是临时变量太混乱,难以替换。这时候Replace Method with Method Object就该登场了。
参数带来的问题稍微少一些,前提是你不在函数内赋值给它们。若是你这样作了,请使用Remove Assignments to Parameters。
函数分解完毕后,我就能够知道如何让它工做得更好。也许某些算法还能够改进,让代码更清晰。这就须要Substitute Algorithm来引入更清晰的算法。
To Be Continued...