热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

IntelliJIDEA复杂的重构技巧

本文作者:ice1000原文链接:http:ice1000.org20171221IDEARefactoring重构是IDE给人类生活带来便利的一个

本文作者:ice1000原文链接:http://ice1000.org/2017/12/21/IDEARefactoring/

重构是 IDE 给人类生活带来便利的一个重要方面。但是 IDE 永远不是我们肚子里的蛔虫,有时我们会有复杂到 IDE 不可能直接提供的重构需求。

下面我来告诉大家怎么利用有限的 IDE 重构功能, ~~创造无限的价值~~ 处理复杂的情况。

复习一下快捷键

先复习一下快捷键吧,我们这次就看两个就好。

inline

这个叫 inline 的东西快捷键是 Ctrl+Alt+n。这个东西的作用是把当前光标上的东西,在代码级别内联掉。

按下这个快捷键后,会看到一个弹窗(这个是 inline 一个 Kotlin 方法的弹窗,对于 Java 还多几个选项。 不过这都不是重点啦):

?wx_fmt=png&wxfrom=5&wx_lazy=1

我们都默认选第一个,就是在 inline 之后删除被 inline 的东西,第二个是 inline 后保留。如果你是在调用处而不是定义处这么搞,第三个选项就可以选,是只 inline 这一处。

我们一般不管,使用第一个。

rename

这个我就不多介绍了,应该是最常用的快捷键之一了: Shift+F6 。

删除一个被多次引用的空函数

场景

我们知道, IntelliJ 会把 “没有被用到的函数” 标灰(这个 “没有被用到” 的定义其实蛮复杂的,比如你实现了一个接口, 那么这个接口的方法即使没被调用也不会被标灰。这里我就不纠结这个细节了),并且会给出 “Safe delete” 的提示。这往往不是我们想要的,因为我们看到这个东西的时候, 多半都是刚写完一个函数还没来得及调用的时候。

而我们有时在重构的时候,一个函数里面的东西被全部移出去后,这个函数体就是空的了,而它仍然在多处被调用。我们这时想删除这个函数,以及它的所有调用处。

 
  
  1. fun SymbolList.addGetSetFunction() {

  2. }

比如这个,我在重构 Lice 的时候,就产生了很多上面这种东西。这个函数被调用了,所以 IntelliJ IDEA 不会给出 “Safe delete” 的选项。

虽然语言是 Kotlin ,但是这就是一个朴素的函数声明,我觉得不需要进行进一步的说明。

 
  
  1. private fun initialize() {

  2.  addDefines()

  3.  addGetSetFunction()

  4.  addControlFlowFunctions()

它像这样被不停调用着。

当然,你可以按下 Ctrl 然后点击这个函数,再一处一处地删除。

解决方法

不过我们为什么不试试直接 inline 掉它呢?

 
  
  1. private fun initialize() {

  2.  addDefines()

  3.  addControlFlowFunctions()

它的函数体本身就是空的,所以说 inline 掉后,每个调用处就啥都没了。

修改大量出现的相同结构

场景

比如,我们有这样的,自己用的库代码(为了让更多人看懂,我在这里使用了 Java):

 
  
  1. // code 0

  2. class Val {

  3.  private Object o;

  4.  public Object getO() { return o; }

  5. }

  6. interface Node { Val eval(); }

然后我们可以通过 someNode.eval().getO() 来获取一个 Object ,对吧。然后想象一下我们有这样的业务代码:

 
  
  1. // code 1

  2. Object a = xxx.getNode().eval().getO();

  3. xxx.use(yyy.eval().getO());

  4. Val bla = blablabla.eval();

  5. switch (bla.getO().toString()) {

  6.  case "2333":

  7.    break;

  8. }

  9. ...

这是我们现在的代码。

问题

然后我们经过一番小重构,把刚刚的库代码重构成了这样:

 
  
  1. // code 0

  2. interface Node { Object eval(); }

直接把 Val 去掉了,然后让这个 eval() 直接返回原本装在 Val 里面的变量,然后 Node 的各种实现也都改了。这时候我们的业务代码已经是一坨红色了。

?wx_fmt=png

我们想批量去掉这个 .getO() 的结构,应该怎么办呢?

首先我们不考虑查找替换,因为

  • 有这种结构的文件很多(假设有一万个),很麻烦(不过 IntelliJ IDEA 有 “Replace in path” 功能)

  • 有很多其他的叫 getO() 但不需要被重构掉的函数,会受到波及(这才是最主要的)

这也是很常见的原因,对吧。

这时我们就需要技巧性地重构了。

解决方法

首先,我们先把库代码中的 Node 临时性地改成这个样子(也就是说,临时性地把 Val 弄回来,只是实现变得不一样了):

 
  
  1. // code 0

  2. class Val {

  3.  public Object getO() { return this; }

  4. }

  5. interface Node { Val eval(); }

注意这里的 getO() 被改成了返回 this

这时我们刚刚的代码中, .getO() 上的红色已经消失了(毕竟这几乎就是改之前的样子)。

然后,我们对 public Object getO() { return this; } 中的 getO() 使用 inline, 这样所有的 .getO() 结构就被消除了(想想为什么,很简单的道理):

?wx_fmt=png

然后我们 把 Val 重命名为 Object ,然后直接删除 ,这样剩下的代码中用到 Val 的地方也就全部变成了 Object , 也就是我们所期望使用的那个类型啦。

另一种情况

上面说的,是针对 “批量删除对于一个方法的调用” 的解决方案。但我们有时不是想删除,而是增加。这怎么办嘞?

比如,我们现在有上面那段重构完了的代码(which 没有 getO() )。我们现在要把每一处 eval() 后面加上 toString() (反正就是需要加一层方法调用)。

这个也很好解决,我们只需要先把 eval() 随便改成(不是重命名,是直接改)另外一个名字(比如 rua):

 
  
  1. // code 0

  2. interface Node { Object rua(); }

然后我们可以看到业务代码全红了:

?wx_fmt=png

然后我们再写一个叫 eval 的方法,里面返回这个 rua 的调用结果再 toString() (就是加上你要的那个方法调用):

 
  
  1. // code 0

  2. interface Node {

  3.  default Object eval() {

  4.    return rua().toString();

  5.  }

  6.  Object rua();

  7. }

这时业务代码已经不报错了。我们再对这个 eval() 进行 inline ,之后就是这个样子的了:

?wx_fmt=png

然后再把 rua() 使用 IntelliJ 的重命名功能改成之前的 eval() ,就一切照旧啦。

本文完

祝大家圣诞节快乐。

 
  
  1.  __________________________________________________

  2. |                    _                             |

  3. | /|,/ _   _ _      / ` /_  _ .  _ _/_ _ _   _    _|

  4. |/  / /_' / / /_/  /_, / / / / _\  /  / / / /_| _\ |

  5. |             _/                                   |

  6. |                ~~** ice1000 **~~                 |

  7. |__________________________________________________|

  8.                       ___

  9.                    /`   `'.

  10.                   /   _..---;

  11.                   |  /__..._/  .--.-.

  12.                   |.'  e e | ___\_|/____

  13.                  (_)'--.o.--|    | |    |

  14.                 .-( `-' = `-|____| |____|

  15.                /  (         |____   ____|

  16.                |   (        |_   | |  __|

  17.                |    '-.--';/'/__ | | (  `|

  18.                |      '.   \    )"";--`\ /

  19.                \        ;   |--'    `;.-'

  20.                |`-.__ ..-'--'`;..--'`

  21. :*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*

这是一个来自 coding.net 的惊喜,我在推送代码的时候看到的:

?wx_fmt=png

推荐阅读

那些有趣又实用的开源人工智能项目 Top 10

自学编程需要注意什么?

Spring干货汇总(含Spring Boot与Spring Cloud)

IntelliJ IDEA插件系列:五大装逼神器

我最常用的Intellij IDEA快捷键

最好用的 IntelliJ 插件 Top 10


点击 “阅读原文” 看看本号其他精彩内容


推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
author-avatar
和谐啄木鸟
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有