IntelliJ IDEA 复杂的重构技巧(二)

转载

上次我说了一些 “复杂的重构技巧” ,讲的是一些使用 IntelliJ 的简单功能实现复杂的重构需求的技巧。 看到大家的反响之后我就感觉那个可能不大亲民,因为很多人连 inline 这功能都不知道(那岂不是把 IntelliJ 用成了记事本), 于是我决定再写一篇讲讲 IntelliJ 已经提供好了的一些复杂的重构功能。

这就不再是需要自己进行奇奇怪怪的操作的教程了,就会亲民得多。

从方法中提取方法

这是用来快速复用一段代码的功能,名叫 “Extract Method” 。

比如,我现在有这么一段业务代码(顺带一提,这是在 Java 调用动态语言 API 时能使用的最健壮的处理数值类型的方法):


  1. liceEnv.defineFunction("run-later", ((metaData, nodes) -> {
  2. Number time = (Number) nodes.get(0).eval();
  3. Consumer<Node> nodeConsumer = Node::eval;
  4. if (time != null) runLater(time.longValue(), () -> {
  5. for (int i = 1; i < nodes.size(); i++) {
  6. // 截图之前写的时候脑抽了,这个是后来改的
  7. nodeConsumer.accept(nodes.get(i));
  8. }
  9. });
  10. return new ValueNode(null, metaData);
  11. }));
  12. ...

为了效率考虑,你决定不使用 subList(1, nodes.size()).forEach 而是使用 for 循环。

然后你突然发现,这个 “遍历一个集合除了第一个元素之外的元素” 操作在你的代码里面已经被调用了很多次了。

于是你决定贯彻 “非极端性 DRY 原则” ,把这坨代码复用起来。

我们仔细观察一下。

这坨代码中,直觉上,我们希望可以通过形如

nodes.forEachExceptFirst(someOperation::accept)

的代码来一行处理这个操作的(不懂方法引用的请退群),但是这个 forEachExceptFirst 是不存在的。

所以我们想自己造一个。

这时候我们就应该使用 IntelliJ IDEA 提供的 Extract method 功能了。

首先选中那一堆代码,然后按下 Ctrl+Alt+m,看到这么一个窗口。

img

然后我们在 “Name” 那一栏输入 forEachExceptFirst ,也就是我们想提取的函数的函数名;然后回车。

我们可以看到,代码变成了这样:


  1. liceEnv.defineFunction("run-later", ((metaData, nodes) -> {
  2. Number time = (Number) nodes.get(0).eval();
  3. Consumer<Node> nodeConsumer = Node::eval;
  4. if (time != null) runLater(time.longValue(), () -> {
  5. forEachExceptFirst(nodes, nodeConsumer);
  6. });
  7. return new ValueNode(null, metaData);
  8. }));
  9. ...

我们可以看看它生成的这个 forEachExceptFirst 方法:


  1. private void forEachExceptFirst(
  2. List<? extends Node> nodes,
  3. Consumer<Node> nodeConsumer) {
  4. for (int i = 1; i < nodes.size(); i++) {
  5. nodeConsumer.accept(nodes.get(i));
  6. }
  7. }

然后你就可以在其他地方使用这个方法了。

我们可以给它加上 JetBrains annotations:


  1. private void forEachExceptFirst(
  2. @NotNull List<@NotNull ? extends @NotNull Node> nodes,
  3. @NotNull Consumer<@NotNull Node> nodeConsumer) {
  4. for (int i = 1; i < nodes.size(); i++) {
  5. nodeConsumer.accept(nodes.get(i));
  6. }
  7. }

当然加这么多意义不大,对 Node 类型的 @NotNull 注解是可以去掉的。

撤回这个操作的话,请使用上一篇博客所大量使用的 inline 功能。

从类中提取接口

比如,我们有这么一个 Java 类(最近突然觉得,对类型的注解应该比可见性修饰符更靠近类型(比如在一个方法中, 我就可以用这种方法来区分对返回类型的注解(比如 @NotNull)和对方法本身的注解(比如 @Override)), 所以就有了这么个把注解写在可见性修饰符后面的奇怪的写法,希望读者不要介意这一点)。


  1. public class Marisa {
  2. // blablabla
  3. public Marisa(@NotNull Touhou game) {
  4. // blablabla
  5. }
  6. public @NotNull ImageObject player() {
  7. return player;
  8. }
  9. public @NotNull List<@NotNull ImageObject> bullets() {
  10. return makeLeftRightBullets(player, mainBullet);
  11. }
  12. public void dealWithBullet(@NotNull ImageObject bullet) {
  13. // blablabla
  14. }
  15. }

代码中省去了一些对文章不重要的细节。

然后我们可以在类名上右键,然后找到这个东西:

img

这样我们会看到一个窗口,里面的东西还挺复杂的:

img

首先我们在 “Interface name” 那里填我们想抽取的接口的名字,比如刚刚的那个类 Marisa ,就很适合GensokyoManagement (毕竟魔理沙是幻想乡两位城管之一嘛,又因为城管的翻译是 Urban management) 这个名字的接口。

然后我们希望把这三个方法都抽取到接口里面去,于是就勾选下面的三个方法。请根据实际需求勾选需要抽取的方法。

最后回车。

这时候 IntelliJ IDEA 会询问你,是否 “尽可能在这个类被使用的地方,把这个类的类型改成接口的类型”。

img

这是一种很好的作法,比如我们会倾向于把

LinkedList<Marisa> gensokyoManagements = new LinkedList<Marisa>();

写成

List<GensokyoManagement> gensokyoManagements = new LinkedList<Marisa>();

,对不对吖。

这里这个提示就是问你要不要这么换一波的。这个就看需求了,另外建议取消勾选下面的 “Preview usages to be changed”。

最后我们就提取出来了这么个玩意(这里只有三个方法所以生成的代码很少,看起来不是很高大上, 如果你实现了一种操作比较多的数据结构(比如线段树啊,各种图啊树啊)再这么来一波,就能生成一大坨):


  1. public interface GensokyoManagement {
  2. @NotNull ImageObject player();
  3. @NotNull List<@NotNull ImageObject> bullets();
  4. void dealWithBullet(@NotNull ImageObject bullet);
  5. }

然后我们就可以再写其他类,比如:


  1. public class Reimu implements GensokyoManagement {
  2. }

然后让 IntelliJ IDEA 自动生成之前那些方法,然后我们就可以愉快地写实现啦。

接口与实现间的互相发送代码

我们还有很多可以做的事情,比如我们现在给 Marisa 类加了新方法作为新功能,然后我们想给 Reimu 也加上, 并把这个方法作为 GensokyoManagement 的一个抽象方法之一(接口的方法是默认抽象的,别因为省了 abstract 修饰符就以为不是了):


  1. public @NotNull List<@NotNull ImageObject> spellCard() {
  2. return masterSpark();
  3. }

我们可以这样,在新方法上右键,然后这么选:

img

这样我们会看到一个窗口,里面的东西不怎么复杂:

img

只需要勾选我们要送给接口(或者父类)的方法,然后回车就好了。

IntelliJ IDEA 会给你加上 @Override 修饰符,和生成新的抽象方法。

然后我们就可以跳到 Reimu 类,让 IntelliJ IDEA 生成一个空实现,然后接着写啦。

本文作者:ice1000

原文链接:http://ice1000.org/2017/12/25/IDEARefactoring2/
版权归作者所有,转载请注明出处

IntelliJ IDEA 复杂的重构技巧的更多相关文章

  1. Java常见重构技巧 - 去除不必要的!=null判断空的5种方式,很少有人知道后两种

    常见重构技巧 - 去除不必要的!= 项目中会存在大量判空代码,多么丑陋繁冗!如何避免这种情况?我们是否滥用了判空呢?@pdai 常见重构技巧 - 去除不必要的!= 场景一:null无意义之常规判断空 ...

  2. 常见重构技巧 - 5种方式去除多余的if else

    常见重构技巧 - 去除多余的if else 最为常见的是代码中使用很多的if/else,或者switch/case:如何重构呢?方法特别多,本文带你学习其中的技巧. 常见重构技巧 - 去除多余的if ...

  3. RefactoringGuru 代码异味和重构技巧总结

    整理自 RefactoringGuru 代码异味 --什么?代码如何"闻味道"?? --它没有鼻子...但它肯定会发臭! 代码膨胀 [代码膨胀]是代码.方法和类,它们的规模已经增加 ...

  4. IntelliJ IDEA重构技巧收集

    https://segmentfault.com/a/1190000002488608(重命名文件) http://www.jianshu.com/p/ab298b46bf50(快速生成方法) htt ...

  5. Intellij IDEA快捷键与使用技巧一览表

    Intellij IDEA快捷键 Ctrl+Shift + Enter,语句完成 "!",否定完成,输入表达式时按 "!"键 Ctrl+E,最近的文件 Ctrl ...

  6. IntelliJ IDEA 常用快捷键和技巧

    IntelliJ Idea 常用快捷键列表 Alt+回车 导入包,自动修正Ctrl+N  查找类Ctrl+Shift+N 查找文件Ctrl+Alt+L  格式化代码Ctrl+Alt+O 优化导入的类和 ...

  7. 前端页面重构技巧总结TIP【持续更新...】

    本文均为项目实战经验,要求兼容至IE8,所以以下内容均为兼容代码,欢迎各位小伙伴批评指教.其实重构页面是一门学问,看似简单,却暗藏很多学问.实际项目中页面的重构有以下几点最基本需求: 1.需要使用合理 ...

  8. IntelliJ IDEA编辑器的使用技巧

    目录 1. 使用技巧 1. 跳转小工具 2. 无处不在的跳转 3. 精准搜索 4. 列操作: 5. live template 6. postfix 7. ALT+ENTER智能提示,代码优化 8. ...

  9. [效率神技]Intellij 的快捷键和效率技巧|系列一|常用快捷键

    Intellij 是个功能强大的IDE,这里只讲window下社区版的Intellij. 1. 常用快捷: Alt+回车 导入包,自动修正Ctrl+N   查找类Ctrl+Shift+N 查找文件Ct ...

随机推荐

  1. [题解] [CF451E] Devu and Flowers

    题面 题解 就是一个求\(\sum_{i= 1}^{n}x _ i = m\)的不重复多重集的个数, 我们可以由容斥原理得到: \[ ans = C_{n + m - 1}^{n - 1} - \su ...

  2. TCP被动打开 之 第二次握手-发送SYN+ACK

    假定客户端执行主动打开,发送syn包到服务器,服务器执行完该包的第一次握手操作后,调用af_ops->send_synack向客户端发送syn+ack包,该回调实际调用tcp_v4_send_s ...

  3. tp5 多条件查询

    $where['rc_category_id'] = array('eq',$cid); $where['rc_territory_ids'] = ['like', '%'.$tid.'%']; $l ...

  4. Java——重写hashCode()和euqals()方法

    1.顺序表的问题 查找和去重效率较低 对于这样的顺序表来说,如果需要查找元素,就需要从第一个元素逐个检查,进行查找.对于需要去重的存储来说,每次存入一个元素之前,就得将列表中的每个元素都比对一遍,效率 ...

  5. RN性能优化(重新探索react吧)

    最近做RN遇到了一些性能瓶颈,逼着自己不得不做一些优化 已经做过,或者尝试过得优化方案: 1.点击效果防止重复点击. 2.左右两边分别用两个异步栈进行更新,这样能让右边的缓慢不影响左边的更新. 3.I ...

  6. vue 如何修改element.style样式

    在css样式表里面加入一个背景样式background:#FFFFFF ! important

  7. 数据库开源框架之ormlite

    主页: http://ormlite.com/ 配置: 添加以下依赖 * compile 'com.j256.ormlite:ormlite-android:4.48' * compile 'com. ...

  8. springboot2.0数据制作为excel表格

    注意:由于公司需要大量导出数据成excel表格,因此在网上找了方法,亲测有效. 声明:该博客参考于https://blog.csdn.net/long530439142/article/details ...

  9. 几行python代码解决相关词联想

    日常生活中经常会遇到相关词联想的问题,也就是说输入一个词汇,把相关的词汇查询出来,听起来这个做法也不是太难,但如何去积累那么多的词汇,再用好的算法将相关内容联系起来,本身还是不简单的.笔者认为最简单的 ...

  10. flask 学习 (五)

    之前照着书去做的时候经常出现一些小问题,由于对于flask核心内容还不甚了解,我觉定先从更简单的做起,再根据别的需要对搭建的网站进行扩展. 上网找了一下,发现这位http://zhanghonglun ...