重构——重新组织你的函数

来源:互联网 发布:彩视官网 软件 编辑:程序博客网 时间:2024/05/11 05:35

重构——重新组织你的函数

1.    Extract Method

将这段代码放进一个独立函数中,并让函数名称解释该函数的用途。

void printOwing(double amount)

{

         printBanner();

 

         //print details

         System.out.println(“name:” + _name);

         System.out.println(“amount “ + _amount);

}

 

void printOwing(double amount)

{

         printBanner();

         printDetails(amount);

}

void printDetails(double amount)

{

         System.out.println(“name:” + _name);

         System.out.println(“amount “ + amount);

}

 

作法:

创造一个新函数,根据这个函数的意图来给它命名(以它[做什么]来命名,而不是以它[怎样做]命名)

ð 即使你想要提炼的代码非常简单,例如只是一条消息或一个函数调用,只要新函数的名称能够以更好方式昭示代码意图,你也应该提炼它。但如果你想不出一个更有意义的名称,就别动。

将提炼出的代码从源函数拷贝到新建的目标函数中。

仔细检查提炼出的代码,看看其中是否引用了[作用域限于源函数]的变量(包括局部变量和源函数参数)

检查是否有[仅用于被提炼码[的临时变量。如果有,在目标函数中将它们声明为临时变量。

检查被提炼码,看看是否有任何局部变量的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼码处理为一个查询,并将结果赋值给相关变量。如果很难这样做,或如果被修改的变量不止一个,你就不能仅仅将这段代码原封不动地提炼出来。你可能需要先使用Split Temporary Variable,然后再尝试提炼。也可以使用Replace Temp with Query将临时变量消灭掉。

将被提炼码中需要读取的局部变量,当作参数传给目标函数。

处理完所有局部变量之后,进行编译。

在源函数中,将被提炼码替换为[为目标函数的调用]

ð 如果你将任何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼码的外围。如果是,现在你可以删除这些声明式了。

编译,测试。

 

2.    Inline Method

在函数调用点插入函数本体,然后移除该函数。

int getRating()

{

         return (moreThanFiveLateDeliveries()) ? 2 : 1;

}

Boolean moreThanFiveLateDeliveries()

{

         return _numberOfLateDeliveries > 5;

}

 

int getRating()

{

         return (_numberOfLateDeliveries > 5) ? 2 : 1;

}

 

作法:

检查函数,确定它不具多态性。

ð 如果subclass继承了这个函数,就不要将此函数inline化,因为subclass无法覆写一个根本不存在的函数。

找出这个函数的所有被调用点。

将这个函数的所有被调用点都替换为函数本体。

编译,测试。

删除该函数的定义。

 

3.    Inline Temp

将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。

double basePrice = anOrder.basePrice();

return (basePrice > 1000);

 

return (anOrder.basePrice() > 1000);

 

作法:

如果这个临时变量并未被声明为final,那就将它声明为final,然后编译。

ð 这可以检查该临时变量是否真的只被赋值一次。

找到该临时变量的所有引用点,将它们替换为[为临时变量赋值]之语句中的等号右侧表达式。

每次修改后,编译并测试。

修改完所有引用点之后,删除该临时变量的声明式和赋值语句。

编译,测试。

 

4.    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; }

 

作法:

找出只被赋值一次的临时变量。

ð 如果某个临时变量被赋值超过一次,考虑使用Split Temporary Variable将它分割成多个变量。

将该临时变量声明为final

编译。

ð 这可确保该临时变量的确只被赋值一次。

[对该临时变量赋值]之语句的等号右侧部分提炼到一个独立函数中。

ð 首先将函数声明为private。日后你可能会发现有更多class需要使用它,彼时你可轻易放松对它的保护。

ð 确保提炼出来的函数无任何连带影响,也就是说该函数并不修改任何对象内容。如果它有连带影响,就对它进行Separate Query from Modifier

编译,测试。

在该临时变量身上实施Inline Temp

 

5.    Introduce Explaining Variable

将该复杂表达式的结果放进一个临时变量,以此变量名称来解释表达式用途。

if ( (platform.toUpperCase().indexOf(“MAC”) > -1) &&

   (browser.toUpperCase().indexOf(“IE”) > -1) &&

   wasInitialized() && resize > 0 )

{

         // do something

}

 

final boolean isMacOs = platform.toUpperCase().indexOf(“MAC”) > -1;

final boolean isIEBrowser = browser.toUpperCase().indexOf(“IE”) > -1;

final Boolean wasResized = resize > 0;

if ( isMacOs && isIEBrowser && wasInitialized() && wasResized)

{

         // do something

}

 

作法:

声明一个final临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给它。

将表达式中的[运算结果]这一部分,替换为上述临时变量。

ð 如果被替换的这一部分在代码中重复出现,你可以每次一个,逐一替换,

编译,测试。

重复上述过程,处理表达式的其它部分。

 

6.    Split Temporary Variable

针对每次赋值,创造一个独立的、对应的临时变量。

double temp = 2 * (_height + _width);

System.out.println(temp);

temp = _height * _width;

System.out.println(temp);

 

final double perimeter = 2 * (_height + _width);

System.out.println(perimeter);

final double area = _height * _width;

System.out.println(area);

 

作法:

[待剖解]之临时变量的声明式及其第一次被赋值处,修改其名称。

ð 如果稍后之赋值语句是[i = i + 某表达式]形式,就意味这是全集用临时变量,那么就不要剖解它。集用临时变量的作用通常是累加、字符串接合、写入stream或者向群集添加元素。

将新的临时变量声明为final

以该临时变量之第二次赋值动作为界,修改此前对该临时变量的所有引用点,让它们引用新的临时变量。

在第二次赋值处,重新声明原先那个临时变量。

编译,测试。

逐次重复上述过程。每次都在声明处临时变量易名,并修改下次赋值之前的引用点。

 

7.    Remove Assignments to Parameters

以一个临时变量取代该参数的位置。

int discount (int inputVal, int quantity, int yearToDate)

{

         if (inputVal > 50) inputVal -= 2;

 

int discount(int inputVal, int quantity, int yearToDate)

{

         int result = inputVal;

         if (inputVal > 50) result -= 2;

 

作法:

建立一个临时变量,把待处理的参数值赋予它。

[对参数的赋值动作]为界,将其后所有对此参数的引用点,全局替换为[对此临时变量的引用动作]

修改赋值语句,使其改为对新建之临时变量赋值。

编译,测试。

ð 如果代码的语义是pass by reference,请在调用端检查调用后是否还使用了这个参数。也要检查有多少个pass by reference参数[被赋值后又被使用]。请尽量只以return方式返回一个值。如果需要返回的值不只一个,看看可否把需要返回的大堆数据变成单一对象,或干脆为每个返回值设计对应的一个独立函数。

 

8.    Replace Method with Method Object

将这个函数放进一个单独对象中。如此一来局部变量就成了对象内的值域。然后你可以在同一个对象中将这个大型函数分解为数个小型函数。

class Order...

 double price()

 {

         double primaryBasePrice;

         double secondaryBasePrice;

         double tertiaryBasePrice;

         // long computation;

         ...

}

 

 

作法:

建立一个新class,根据[待被处理之函数]的用途,为这个class命名。

在新class中建立一个final值域,用以保存原先大型函数所驻对象。我们将这个值域称为[源对象]。同时,针对原函数的每个临时变量和每个参数,在新class中建立一个个对应的值域保存之。

在新class中建立一个构造函数,接收源对象及原函数的所有参数作为参数。

在新class中建立一个compute()函数。

将原函数的代码拷贝到compute()函数中。如果需要调用源对象的任何函数,请以[源对象]值域调用。

编译。

将旧函数的函数本体替换为这样一条语句:[创建上述新class的一个新对象,而后调用其中的compute()函数]。

 

9.    Substitute Algorithm

将函数本体替换为另一个算法。

String foundPerson(string[] people)

{

         for(int i = 0; i < people.length; i++)

         {

                   if(people[i].equals(“Don”))

                            return “Don”;

                   if(people[i].equals(“John”))

                            return “John”;

                   if(people[i].equals(“Kent”))

                            return “Kent”;

         }

         return “”;

}

 

String foundPerson(string[] people)

{

         List candidates = Arrays.asList(new String[] {“Don”, “John”, “Kent”});

         for(int i=0; i < people.length; i++)

                   if(candidates.contains(people[i]))

                            return people[i];

         return “”;

}

 

作法:

准备好你的另一个算法,让它通过编译。

针对现有测试,执行上述的新算法。如果结果与原本结果相同,重构结束。

如果测试结果不同于原先,在测试和调试过程中。以旧算法为比较参照标准。

ð 对于每个test case,分别以新旧两种算法执行,并观察两者结果是否相同。这可以帮助你看到哪一个test case出现麻烦,以及出现了怎样的麻烦。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 二年级孩子不爱写作业怎么办 孩子喜欢的朋友家长不喜欢怎么办? 孩子不喜欢上幼儿园家长怎么办 孩子不喜欢家长学佛怎么办呢? 学生家长讨厌老师老师该怎么办 孩子不爱去幼儿园总是哭怎么办 孩子学习一点都不主动怎么办 孩子怕老师不想上学怎么办 幼儿园孩子怕老师不想上学怎么办 孩子在幼儿园怕老师怎么办 幼儿园老师对孩子有歧视怎么办 儿子在幼儿园受到老师歧视怎么办 我把老师骂了怎么办 孩子讨厌老师骂老师怎么办 幼儿园老师讨厌一个孩子怎么办 幼儿园老师对孩子不好怎么办 高中分班讨厌班主任怎么办 把孩子老师惹了怎么办 高三班主任第一节课应该怎么办 高三孩子与班主任不和怎么办 孩子很害怕一件事怎么办 吼完孩子觉得很害怕怎么办 小朋友在幼儿园被欺负怎么办 老师总找孩子茬怎么办 小孩说幼儿园老师不喜欢她怎么办 小孩在学校老师不喜欢怎么办 好哭的孩子老师怎么办 小孩哭的犟住怎么办 2-3小孩脾气很犟怎么办 生了孩子没奶怎么办 二年级孩子写作业慢怎么办 六年级下册数学解决问题差的怎么办 孩子五年级了学习成绩不好怎么办 孩子做错事不承认怎么办 二年级小孩拼音差怎么办 小学三年级数学60多分怎么办 孩子三年级了数学不开窍怎么办 初二数学太差该怎么办 孩子语文阅读理解能力差怎么办 5年级数学不开窍怎么办 一年级孩子数学不开窍怎么办