重新组织你的函数 Compsing Methords
一 Extract Methord
动机:Extract Methord是最常用的重构手法之一,当我看到一个过长函数或则一段需要注释才能让人理解用途的代码的时候,我会将这段代码放到独立的函数中去。如果函数的粒度都很小(finely grained)那么函数间彼此服用的机会就更大,其次,这会使高层函数读起来就像一系列注释,而且函数的复写(override)也会容易些。一个函数长度不是问题,关键在于,函数名称和函数本体之间的语义距离 (semantic distance),如果提炼的动作可以强化代码的清晰度,那就去做,就算函数名比提炼出的代码本身还长也无所谓。
做法:
1.创造一个新的函数,根据这个函数的意图来命名它,用做什么来命名而不是怎么做,若果你想不出一个更好的名字就不要动,记住小步前进。
2.将提炼出的函数重原函数拷贝到目标函数
3.检查提炼的代码中是否有引用了的变量
4.检查是否有临时变量,如果有在目标函数将他们声明为临时变量
5.检查被提炼的代码是否有局部变量的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼的代码处理为一个查询,并将结果赋值给相关变量。如果很难这样做或修改的变量不只一个,你可能要线使用 Split Temporay variable 然后在尝试提炼。也可以使用Replace Tem With Query将临时变量消灭掉
6.将被提炼的代码中需要读取的局部变量,当做参数穿给目标函数
7.处理完所有局部变量之后进行编译
8.在原函数将被提炼的代码转为对目标函数的调用
9编译测试
eg:
a. 无局部变量 NoLocalConnectionVariables
private function printOwing():void { var outstanding:Number = 0.0; var e:Array = _orders; //trace the Banner trace("***************"); trace("*Customer Owes**"); trace("***************"); //calculate outstanding for each(var i:Object in e) { outstanding+= i.getAmount(); } //trace details trace("name: "+ _name); trace("outstanding : " + outstanding ); }
==>
private function printOwing():void { var outstanding:Number = 0.0; var e:Array = _orders; printBanner(); //calculate outstanding for each(var i:Object in e) { outstanding+= i.getAmount(); } //trace details trace("name: "+ _name); trace("outstanding : " + outstanding ); } private function printBanner ():void { trace("***************"); trace("*Customer Owes**"); trace("***************"); }
b. 无局部变量 NoLocalConnectionVariables
如果这么简单那么重构的难点在哪呢,对的就在局部变量,包括传进原函数的参数和原函数所声明的临时变量。局部变量的作用域仅限于原函数,所以当我使用 Extract Methord 时,必须花额外的功夫去处理这些变量。某些时候他们会妨碍我们,使我们无法重构。
局部变量的最简单的情况就是,被提炼的码只是读取这些变量的值而不去修改他们,这种情况我可以直接当参数穿给目标函数。
private function printOwing():void { var outstanding:Number = 0.0; var e:Array = _orders; printBanner(); //calculate outstanding for each(var i:Object in e) { outstanding+= i.getAmount(); } //trace details trace("name: "+ _name); trace("outstanding : " + outstanding ); } private function printBanner():void { trace("***************"); trace("*Customer Owes**"); trace("***************"); }
==》
private function printOwing():void { var outstanding:Number = 0.0; var e:Array = _orders; printBanner(); //calculate outstanding for each(var i:Object in e) { outstanding+= i.getAmount(); } printDetails (outstanding ); } private function printBanner():void { trace("***************"); trace("*Customer Owes**"); trace("***************"); } private fuction printDetails (outstanding :Number):void { trace("name: "+ _name); trace("outstanding : " + outstanding ); }
c. 对局部变量在赋值,如果被提炼码对局部变量赋值,问题就变得复杂了,这里只看临时变量的问题,如果你发现原函数的参数被在赋值,因该马上使用 Remove Asssignments 帖哦 Parameters。被赋值的临时变来那个也分为两种情况。比较简单的的情况是:这个变量只在被提炼的代码中,如果是这样,你可以将这个临时变量的声明一到那个被提炼码中,然后一起提炼出去。另一种情况就是,被提炼码之外的的代码也使用了这个变量。这又分两种情况:如果这个变量在被提炼后为在被使用,你只需直接在目标函数中修改她就可以了;如果,被提炼码之后的的代码还使用了这个变量,你就需要,让目标函数返回改变后的值。
private function printOwing():void { var outstanding:Number = 0.0; var e:Array = _orders; printBanner(); //calculate outstanding for each(var i:Object in e) { outstanding+= i.getAmount(); } printDetails(outstanding ); } private function printBanner():void { trace("***************"); trace("*Customer Owes**"); trace("***************"); } private fuction printDetails(outstanding :Number):void { trace("name: "+ _name); trace("outstanding : " + outstanding ); }
==> 提炼计算的代码
private function printOwing():void { printBanner(); var outstanding:Number = getOutSatnding (); printDetails(outstanding ); } private function printBanner():void { trace("***************"); trace("*Customer Owes**"); trace("***************"); } private fuction printDetails(outstanding :Number):void { trace("name: "+ _name); trace("outstanding : " + outstanding ); } private funtion getOutSatnding ():Number{ var e:Array = _orders; var outstanding:Number = 0.0; for each(var i:Object in e) { outstanding+= i.getAmount(); } return outstanding; }