首页 > 代码库 > 重构笔记——以函数对象取代函数

重构笔记——以函数对象取代函数

本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42549977


        在上一篇文章中介绍了“移除对参数的赋值“。本文将介绍“以函数对象取代函数”这种重构手法。

        下面让我们来学习这种重构手法吧。


开门见山


        发现:你有一个大型函数,其中对局部变量的使用使你无法采用“提炼函数”这种重构手法。

        解决:将这个函数放进一个单独对象中,这样,局部变量就成了对象的字段,然后就可以在同一个对象中将这个大型函数分解为多个小型函数。

//重构前
class Order....
	double price(){
		double basePrice;
		double secondaryPrice;
		double thirdaryPrice;
		//compute()
		......
}<
//重构后
class Order...
	double price(){
		return new PriceCalculator(this).compute();
}

class PriceCalculator{
	double basePrice;
	double secondaryPrice;
	double thirdaryPrice;
	
	double compute(){
		//...
	}
}


动机


        在前面的文章中一直在强调小型函数的优美动人。只要将相对独立的代码从大函数提炼出来,就可以大大提高代码的可读性。

        但是,局部变量的存在会增加函数分解的难度。如果一个函数中的局部变量泛滥成灾,那么想分解这个函数是非常困难的。“以查询替换临时变量”手法可以帮助减轻负担,但有时候还是会发现根本无法拆解一个需要拆解的函数。这种情况就应该考虑使用函数对象来解决。本文的重构方法会将所有的局部变量都变成函数对象的字段。然后就可以使用“提炼函数”创造新的函数,从而将原来的大型函数拆解变小。



做法


(1)建立一个新的类,根据待处理函数的用途为其命名。
(2)在新类中建立一个final字段,用以保存原先大型函数所在对象。针对原函数的每个临时变量和每个参数,在新类中建立一个对应的字段保留之。
(3)在新类中建立一个构造函数,接收原对象以原函数的所有参数作为其参数。
(4)在新类中建立一个compute()函数。
(5)将原函数的代码复制到compute()函数中。如果需要调用源对象的任何函数,通过原对象字段调用。
(6)编译,测试。
        由于所有局部变量现在都成了字段,所以你可以任意分解这个大型函数,不必传递任何参数。


示例


        我们从下面的例子开始:
class Account{
		int gamm(int value, int quantity, int year2Date){
			int importValue1 = (value * quantity) + delta();
			int importValue2 = (value * year2Date) + 200;
			if(year2Date - importValue1 >200)
				importValue2-=50;
			int importValue3 = importValue2 * 8;
			//......
			
			return importValue3 - 2 * importValue1;
		}
		//.....
	}
        为了把这个函数变为函数对象,首先需要声明一个新类。在新类中提供final对象保存原对象,对于函数的每个参数和每个临时变量,也以一个字段逐一保留。
	class Gamm{
		private final Account _account;
		private int value;
		private int quantity;
		private int year2Date;
		private int importValue1;
		private int importValue2;
		private int importValue3;
        接下来,加入一个构造函数。
		Gamm(Account source, int inputVal, int quantity, int year2Date){
			this._account = source;
			this.value = http://www.mamicode.com/inputVal;>        现在可以把原本函数搬到compute()中了。函数中任何调用Accout类的地方,都必改用_account字段。
		int compute(){
			importValue1 = (value * quantity) + _account.delta();
			importValue2 = (value * year2Date) + 200;
			if(year2Date - importValue1 >200)
				importValue2-=50;
			importValue3 = importValue2 * 8;
			//......
			
			return importValue3 - 2 * importValue1;
		}
        然后,修改旧函数,让它将工作委托给刚完成的这个函数对象。
	int gamm(int value, int quantity, int year2Date){
		return new Gamm(this,value,quantity,year2Date).compute();
	}
        以上就是本文重构方法的基本原则。其所带来的好处是:现在可以轻松地对compute()函数采取“提炼函数”,而不必担心参数传递的问题。
	//运用提炼函数 不必担心参数问题
	int compute(){
		importValue1 = (value * quantity) + _account.delta();
		importValue2 = (value * year2Date) + 200;
		importantThing();
		importValue3 = importValue2 * 8;
		//......
		
		return importValue3 - 2 * importValue1;
	}

	private void importantThing() {
		if(year2Date - importValue1 >200)
			importValue2-=50;
	}

        本文主要介绍了重构手法——以函数对象取代函数。我们都不喜欢临时变量巨多的方法,那只会让我们迷惑。对于局部变量很多的函数,有必要运用本文的重构方法进行处理,将其转化为函数对象,那样就把临时变量转为函数对象的字段,继而可以进行其它重构方法。
        最后,希望本文对你有所帮助。有问题可以留言,谢谢。(PS:下一篇将介绍重构笔记——替换算法)


重构笔记文章如下

       重构笔记——入门篇

       重构笔记——代码的坏味道(上)

       重构笔记——代码的坏味道(下)

       重构笔记——构筑测试体

       重构笔记——提炼函数

       重构笔记——内联函数

       重构笔记——内联临时变量

       重构笔记——以查询取代临时变量

       重构笔记——引入解释性变量

       重构笔记——分解临时变量

       重构笔记——移除对参数的赋值

重构笔记——以函数对象取代函数