惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Webroot Blog
Webroot Blog
U
Unit 42
A
About on SuperTechFans
宝玉的分享
宝玉的分享
月光博客
月光博客
C
CERT Recently Published Vulnerability Notes
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
P
Privacy & Cybersecurity Law Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Securelist
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
Apple Machine Learning Research
Apple Machine Learning Research
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
B
Blog
I
Intezer
Last Week in AI
Last Week in AI
T
Threat Research - Cisco Blogs
V
V2EX
L
LangChain Blog
AI
AI
G
GRAHAM CLULEY
T
Tor Project blog
人人都是产品经理
人人都是产品经理
D
Docker
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
I
InfoQ
Y
Y Combinator Blog
C
Comments on: Blog
GbyAI
GbyAI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
Vercel News
Vercel News
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿

博客园 - 网络金领

C# WinForm 导出导入Excel/Doc 完整实例教程[使用Aspose.Cells.dll] DataTable的数据批量写入数据库 C# .NET锁屏程序(顺带屏蔽任务管理器) 步步为营 .NET 代码重构学习 十一 - 网络金领 步步为营 .NET 代码重构学习笔记 十 步步为营 .NET 代码重构学习笔记 九 步步为营 .NET 代码重构学习笔记 八 步步为营 .NET 代码重构学习笔记 七 步步为营 .NET 代码重构学习笔记 六、移动函数和移动值域(Move Method And Move Field) 步步为营 .NET 代码重构学习笔记 五、分解函数和替换算法(Replace Method And Substitute Algorithm) 步步为营 .NET 代码重构学习笔记 四、临时变量(Temporary Variable) 步步为营 .NET 代码重构学习笔记 三、内联方法(Inline Method) 步步为营 .NET 代码重构学习笔记 一、为何要代码重构 步步为营 .NET 设计模式学习笔记 二十四、Factory Method(工厂方法模式) 步步为营 .NET 设计模式学习笔记 二十三、Interpreter(解释器模式) 步步为营 .NET 设计模式学习笔记 二十二、Memento(备望录模式) 步步为营 .NET 设计模式学习笔记 二十一、Visitor(访问者模式) 步步为营 .NET 设计模式学习笔记 二十、Mediator(中介者模式) 步步为营 .NET 设计模式学习笔记 十九、Chain of Responsibility(职责链模式)
步步为营 .NET 代码重构学习笔记 二、提炼方法(Extract Method)
网络金领 · 2011-06-02 · via 博客园 - 网络金领

动机(Motivation)

Extract Method是我们最常用的重构手法之一.当我们看见一个过长的函数或一段需要注释才能让人理解用途的代码,我们就会将这段代码放进一个独立函数中.

有数个原因造成我们喜欢简短而有良好命名的函数.首先,如果每个函数的粒度都很小(finely grained),那么函数之间彼此复用的机会就更大;其次,这会使高层函数代码读起来就像一系列注释;再者,如果函数都是细粒度,那么函数的覆写(override)也会更容易些.

的确,如果你习惯看人型函数,恐怕需要一段时间才能适应这种新风格.而且只有当你能给小型函数很好地命名时,它们才能真正起到作用,所以你需要在函数名称下点功夫.人们有时会问,一个函数多长合适?在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离(scmantic distance),如果提炼动作(extracting)可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码还长也无所谓.

作法(Mechanics)

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

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

2、将提炼出的代码从源函数(source)拷贝到新建的目标函数(target)中。

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

4、检查是否有了公用于被提炼代码的临时变量(teemporary variables)如果有,在目标函数中将它声明为临时变量。

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

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

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

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

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

范例(Examples)

1、无局部变量(No Local Variables)

03     public void PrintUserInfo()
05         Console.WriteLine("******************************");
06         Console.WriteLine("************用户信息**********");
07         Console.WriteLine("******************************");
08         string Name = "spring yang";
09         string Age = "26";
10         string Sex = "Man";
11         Console.WriteLine(string.Format("name is {0},age is {1},sex is {2}", Name, Age, Sex));

改为:

03     public void PrintUserInfo()
06         string Name = "spring yang";
07         string Age = "26";
08         string Sex = "Man";
09         Console.WriteLine(string.Format("name is {0},age is {1},sex is {2}", Name, Age, Sex));
12     public void PrintStart()
14         Console.WriteLine("******************************");
15         Console.WriteLine("************用户信息**********");
16         Console.WriteLine("******************************");

2、有局部变量(Using Local Variables)

果真这么简单,这个重构手法的困难点在哪里?是的,就是在局部变量,包括传进源函数的参数和源函数所声明的临时变量。局部变量的作用域仅限于源函数。所以当我使用Extract Method时,必须花费额外功夫去处理这此变量。某些时候它们甚至可能妨碍我,使我根本无法进行这项重构。

局部变量最简单的情况是:被提炼代码只是读取这些变量的值,并不修改它们。这种情况下我可以简单地将它们当作参数传给目标函数。所发我们面对如下函数:

03     public void PrintUserInfo()
05         string Name = "spring yang";
06         string Age = "26";
07         string Sex = "Man";
08         Console.WriteLine(string.Format("name is {0},age is {1},sex is {2}", Name, Age, Sex));
09         Console.WriteLine(string.Format("{0} both year is {1}.", Name, DateTime.Now.AddYears(-int.Parse(Age))));

改为:

03     public void PrintUserInfo()
05         string Name = "spring yang";
06         string Age = "26";
07         string Sex = "Man";
08         PrintInfo(Name, Age, Sex);
11     public void PrintInfo(string Name, string Age, string Sex)
13         Console.WriteLine(string.Format("name is {0},age is {1},sex is {2}", Name, Age, Sex));
14         Console.WriteLine(string.Format("{0} both year is {1}.", Name, DateTime.Now.AddYears(-int.Parse(Age))));

如果局部变量是个对象,而被提炼代码调用了会对该对象造成修改的函数,也可以如法炮制。你同样只需将这个对象作为参数传递给目标函数即可。只有在被提炼代码真的对一个局部变量赋值的情况下,你才必须采取其它措施。

3、对局部变量现赋值(Reassigning)

如果被提炼代码对局部变量赋值,问题就变得复杂了。这里我们只讨论临时变量的问题。如果你发现源函数的参数被赋值,应该马上使用Remove Assignments to Parameters。

03     public void PrintSum()
05         double Result = 0;
06         for (int i = 0; i <= 100; i++)
07             Result += i;
08         Console.WriteLine("From 1 add to 100 result is {0}.", Result);

改为:

03     public void PrintSum()
05         PrintInfo(Sum());
08     public double Sum()
10         double Result = 0;
11         for (int i = 0; i <= 100; i++)
12             Result += i;
13         return Result;
16     public void PrintInfo(double result)
18         Console.WriteLine("From 1 add to 100 result is {0}.", result);

总结

把能够独立的代码块给它独立出来,使代码的粒度变小,可重用性提高。