第2章 重新组织函数(1):提炼函数

1. 提炼函数(Extract Method)

1.1 动机

(1)函数的粒度小,被复用的机会就会更大。而且细粒度的函数覆写也会更容易。

(2)简短而命名良好的函数,会使高层函数读起来就像一系列注释

1.2 做法

(1)创建一个新函数,并根据函数的意图命名(以它“做什么”来命名,而不是以它“怎么做”命名)

(2)将提炼出的代码从源函数复制到新建的目标函数中。

(3)仔细检查提炼出的代码,看看其中是否引用了“作用域限于源函数”的变量(如即是还是使用了源函数中的局部变量或参数)

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

(5)检查被提炼代码段,看看是否有任何局部变量的值被它改变。如果有一个临时变量的值被修改了,看看是否可以被将提炼代码炼处理为一个查询,并将结果赋值给相关变量。如果被修改的变量不止一个,可以先用Split Temporary Variable,然后再尝试提炼。也可以使用Replace Temp with Query将临时变量消灭掉。

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

(7)在源函数中,将被提炼代码段替换为对目标函数的调用。

1.3 范例

//重构前(Java)

//源函数
void printOwing(double previousAmount)
{
    Enumeration e = _orders.elements(); //_orders是类的成员变量
    double outstanding = previousAmount * 1.2;

    //print banner(无局部变量)
    System.out.println("*****************************");
    System.out.println("********Customer Owes********");
    System.out.println("*****************************");

    //calculate outstanding
    while(e.hasMoreElements())
    {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }

    //print details
    System.out.println("name:" + _name);//_name为成员函数
    System.out.println("amount" + outstanding);//outstanding为局部变量
}

//重构后

//经Extract Method 提炼后的函数
void printOwing(double previousAmount)
{
    printBanner();

    double outstanding = getOutstanding(previousAmount * 1.2);

    printDetail(outstanding);
}

//无局部变量,直接提取(函数名以意图命令)
void printBanner()
{
    //print banner
    System.out.println("*****************************");
    System.out.println("********Customer Owes********");
    System.out.println("*****************************");
}

//有局部变量outstanding,将其作为参数传递给目标函数(函数名以意图命令)
void printDetails(double outstanding)
{
    System.out.println("name:" + _name);
    System.out.println("amount" + outstanding);
}

//对局部变量再赋值,分为两种情况:(函数名以意图命令)
//1.该变量只在被提炼代码中使用时,可直接将该变量的声明移到目标函数中
//2.该变量在被提炼代码之个也使用了这个变量,又分两种情况
//   A.变量在被提炼代码之后未再被使用,只需直接在目标函数中修改它便可
//   B.变量在被提炼代码之后还被使用,可让目标函数返回修改后的变量(本例属这情况)
double getOutStanding(double initValue)
{
   //源函数中变量e只在被提炼代码段中用到,可搬到新函数中定义
    Enumeration e = _orders.elements();

    //源函数中outstanding在被提炼代码段内外都被使用,可让目标函数返回该值
    double result = initValue; //outstanding

    //calculate outstanding
    while(e.hasMoreElements())
    {
        Order each = (Order) e.nextElement();
        result += each.getAmount();
    }    

    return result;
}

1.4 思考

(1)Extract Method最大的困难是处理局部变量,而临时变量是其中之一。

(2)可以用Replace Temp with Query去掉所有可去掉的临时变量。

(3)如果很多地方使用了某个临时变量,可用Split Temporary Variable将它变得比较容易替换。

(4)也可以用Replace Method with Method Object引入一个新类也处理临时变量。

(5)参数带来的问题比较容易处理。但如果在函数内赋值给参数了,可以用Remove Assignments to Parameters来处理。

时间: 2024-10-09 23:33:52

第2章 重新组织函数(1):提炼函数的相关文章

第2章 重新组织函数(2):内联函数和内联临时变量

2. 内联函数(Inline Method) 2.1 动机 (1)在函数调用点插入函数本体,然后移除该函数. (2)有时遇到某些函数,其内部代码和函数名称同样清晰易读,采用内联函数可以提供代码的执行速度(因为少掉了函数调用的开销) (3)在Replace Method withd Method Object之前,将一些组织不合理的函数内联到一个大型函数,再从中提炼组织合理的小型函数时效果很好. (4)使用太多的间接层时,特别是一部分函数只是对另一个函数的简单委托时,可同去掉一些无用的间接层. 2

重构改善既有代码的设计--第6章--重新组织函数

第6章 重新组织函数 6.1 Extract Method Long methods,因为包含太多信息和逻辑,不容易处理和修改.所以需要进行Extract Method. (1)场景 当一个函数过长,或者一段代码需要注释才能看懂,就可以考虑将其放入独立函数中. (2)优点 每个函数粒度小,被复用的几率大,被修改的难度也会低一些:高层函数逻辑分明. (3)做法 以"做什么"命名,而不是"怎么做".比如,printDetail(). 将源函数中的某一段提取到目标函数中.

重新组织函数----Extra Method(提炼函数)

你有一段代码可以被组织在一起并独立出来 讲这段代码放在一个独立函数中,并让函数名称解释该函数的用途 范例:无局部变量 void printOwing(double amount){ printBanner(); //printDetail System.out.println("name:"+_name); System.out.println("amount:"+amount); } 重构后代码: void printOwint(double amount){ p

第2章 重新组织函数(4):函数对象、替换算法

8. 以函数对象取代函数(Replace Method with Method Object) 8.1 动机 (1)将一个大型的函数放进一个单独对象中,使局部变量变成对象内的字段,然后可以在同一个对象中将这个大型函数分解为多个小型函数. (2)局部变量的存在会增加函数分解的难度.如果局部变量太多,可以难以使用Replace Temp with Query来处理,这时可以用函数对象来取代 8.2 做法 (1)建立一个新类,根据待处理函数的用途,为这个类命名. (2)在新类中建一个const字段,用

重构改善既有代码设计--重构手法01:Extract Method (提炼函数)

背景: 你有一段代码可以被组织在一起并独立出来.将这段代码放进一个独立函数,并让函数名称解释该函数的用途. void PrintOwing(double amount) { PrintBanner(); //print details Console.WriteLine("name:"+_name); Console.WriteLine("amount:"+_amount); } void PrintOwing(double amount) { PrintBanne

重构笔记——提炼函数

本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42214393         在前面的三篇文章中介绍了重构入门篇.代码的坏味道(上).代码的坏味道(下).本文将正式开启重构之旅.从本文开始在后续的文章中会陆续介绍92种重构手法,每一种重构手法都会对应于一种代码坏味道.在介绍的过程中,每一种重构手法也将对应一篇文章,可能有的重构手法比较简短,但是为了便于整理还是单独将其列为一篇.(PS:不管怎样,我都会坚

第19章 SVC中断方式调用用户函数

第19章 SVC中断方式调用用户函数 RTX基础教程目录

第七章、函数基础之函数的调用05

目录 第七章.函数基础之函数的调用05 一.什么是函数调用? 二.函数调用的三种形式 第七章.函数基础之函数的调用05 一.什么是函数调用? 函数名()就会调用函数,会执行函数体代码,直到碰到return或者完函数体所有代码 运行完函数体内代码,如果没有return就会返回none def foo(): pass print(foo()) 二.函数调用的三种形式 def max_self(x,y): if x>y: return x else: return y \# 1. max_self(1

第七章、函数基础之函数的返回值04

目录 第七章.函数基础之函数的返回值04 一.什么是返回值 二.为什么要有返回值 第七章.函数基础之函数的返回值04 一.什么是返回值 函数内部代码经过一些列逻辑处理获得的结果. def func(): name = 'nick' return name name = func() print(name) nick 二.为什么要有返回值 如果需要在程序中拿到函数的处理结果做进一步的处理,则需要函数必须要有返回值 注意: return是函数结束的标志 return的返回值可以返回任意数据类型 re