最新公告
  • 欢迎您光临极客文库,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • 上次我说了一些 “复杂的重构技巧” ,讲的是一些使用 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.  returnnewValueNode(null, metaData);
    11. }));
    12. ...
    为了效率考虑,你决定不使用 subList(1,nodes.size()).forEach 而是使用 for 循环。
    然后你突然发现,这个 “遍历一个集合除了第一个元素之外的元素” 操作在你的代码里面已经被调用了很多次了。 于是你决定贯彻 “非极端性 DRY 原则” ,把这坨代码复用起来。
    我们仔细观察一下。 这坨代码中,直觉上,我们希望可以通过形如
    1. nodes.forEachExceptFirst(someOperation::accept)
    的代码来一行处理这个操作的(不懂方法引用的请退群),但是这个 forEachExceptFirst 是不存在的。
    所以我们想自己造一个。 这时候我们就应该使用 IntelliJ IDEA 提供的 Extractmethod 功能了。
    首先选中那一堆代码,然后按下 Ctrl+Alt+m,看到这么一个窗口。
    然后我们在 “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.  returnnewValueNode(null, metaData);
    8. }));
    9. ...
    我们可以看看它生成的这个 forEachExceptFirst 方法:
    1. privatevoid forEachExceptFirst(
    2.    List<?extendsNode> 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. privatevoid forEachExceptFirst(
    2.    @NotNullList<@NotNull?extends@NotNullNode> nodes,
    3.    @NotNullConsumer<@NotNullNode> 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. publicclassMarisa{
    2.  // blablabla
    3.  publicMarisa(@NotNullTouhou game){
    4.    // blablabla
    5.  }
    6.  public@NotNullImageObject player(){
    7.    return player;
    8.  }
    9.  public@NotNullList<@NotNullImageObject> bullets(){
    10.    return makeLeftRightBullets(player, mainBullet);
    11.  }
    12.  publicvoid dealWithBullet(@NotNullImageObject bullet){
    13.    // blablabla
    14.  }
    15. }
    代码中省去了一些对文章不重要的细节。
    然后我们可以在类名上右键,然后找到这个东西:
    这样我们会看到一个窗口,里面的东西还挺复杂的:
    首先我们在 “Interface name” 那里填我们想抽取的接口的名字,比如刚刚的那个类 Marisa ,就很适合 GensokyoManagement (毕竟魔理沙是幻想乡两位城管之一嘛,又因为城管的翻译是 Urbanmanagement) 这个名字的接口。
    然后我们希望把这三个方法都抽取到接口里面去,于是就勾选下面的三个方法。请根据实际需求勾选需要抽取的方法。 最后回车。
    这时候 IntelliJ IDEA 会询问你,是否 “尽可能在这个类被使用的地方,把这个类的类型改成接口的类型”。
    这是一种很好的作法,比如我们会倾向于把
    1. LinkedList<Marisa> gensokyoManagements =newLinkedList<Marisa>();
    写成
    1. List<GensokyoManagement> gensokyoManagements =newLinkedList<Marisa>();
    ,对不对吖。 这里这个提示就是问你要不要这么换一波的。这个就看需求了,另外建议取消勾选下面的 “Preview usages to be changed”。
    最后我们就提取出来了这么个玩意(这里只有三个方法所以生成的代码很少,看起来不是很高大上, 如果你实现了一种操作比较多的数据结构(比如线段树啊,各种图啊树啊)再这么来一波,就能生成一大坨):
    1. publicinterfaceGensokyoManagement{
    2.  @NotNullImageObject player();
    3.  @NotNullList<@NotNullImageObject> bullets();
    4.  void dealWithBullet(@NotNullImageObject bullet);
    5. }
    然后我们就可以再写其他类,比如:
    1. publicclassReimuimplementsGensokyoManagement{
    2. }
    然后让 IntelliJ IDEA 自动生成之前那些方法,然后我们就可以愉快地写实现啦。

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

    我们还有很多可以做的事情,比如我们现在给 Marisa 类加了新方法作为新功能,然后我们想给 Reimu 也加上, 并把这个方法作为 GensokyoManagement 的一个抽象方法之一(接口的方法是默认抽象的,别因为省了 abstract 修饰符就以为不是了):
    1. public@NotNullList<@NotNullImageObject> spellCard(){
    2.  return masterSpark();
    3. }
    我们可以这样,在新方法上右键,然后这么选:
    这样我们会看到一个窗口,里面的东西不怎么复杂:
    只需要勾选我们要送给接口(或者父类)的方法,然后回车就好了。 IntelliJ IDEA 会给你加上 @Override 修饰符,和生成新的抽象方法。
    然后我们就可以跳到 Reimu 类,让 IntelliJ IDEA 生成一个空实现,然后接着写啦。

    本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
    极客文库 » IntelliJ IDEA 复杂的重构技巧(二)

    常见问题FAQ

    如果资源链接失效了怎么办?
    本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
    如果用户分享的资源与描述不符怎么办?
    可以联系客服QQ:2580505920,如果要求合理可以安排退款或者退赞助积分。
    如何分享个人资源获取赞助积分或其他奖励?
    本站用户可以分享自己的资源,但是必须保证资源没有侵权行为。点击个人中心,根据操作填写并上传即可。资源所获收益完全归属上传者,每周可申请提现一次。
    如果您发现了本资源有侵权行为怎么办?
    及时联系客服QQ:2580505920,核实予以删除。

    Leave a Reply

    Hi, 如果你对这款资源有疑问,可以跟我联系哦!

    联系发布者

    Leave a Reply

    Hi, 如果你对这款资源有疑问,可以跟我联系哦!

    联系发布者
    • 101会员总数(位)
    • 3672资源总数(个)
    • 0本周发布(个)
    • 0 今日发布(个)
    • 124稳定运行(天)

    欢迎加入「极客文库」,成为原创作者从这里开始!

    立即加入 了解更多
    成为赞助用户享有更多特权立即升级