Simple Custom Refactoring in IntelliJ

2019-03-24 05:30发布

问题:

This question is a follow-up for this.

Say I have some class Foo.

class Foo {
    protected String x = "x";

    public String getX() {
        return x;
    }
}

I have a program that uses Foo and violates LoD (Law of Demeter).

class Bar {
    protected Foo foo;

    public Bar() {
        this.foo = new Foo();
    }

    public Foo getFoo() {
        return foo;
    }
}

public static void main(String [] args) {
    Bar bar = new Bar();
    String x = bar.getFoo().getX(); 
}

I can refactor this code to use LoD in two steps.

  1. m bar.getFoo().getX() -> getFooX(bar) (extract to method, also find and replace occurrences)
  2. F6 getFooX(bar) -> bar.getFooX() (move to instance method, also find and replace occurences)

The program that uses Bar no longer violates LoD.

class Bar {
    protected Foo foo;

    public Bar() {
        this.foo = new Foo();
    }

    public Foo getFoo() {
        return foo;
    }

    public String getFooX() {
        return foo.getX();
    }
}

public static void main(String [] args) {
    Bar bar = new Bar();
    String x = bar.getFooX();
}

I am wondering if there is a way to make a custom refactoring method in IntelliJ that would consolidate these two steps into one.

EDIT I got a reply from JetBrains with a link to a pre-existing feature request. Please vote on it if you find this useful!

Hello Michael,

Seems we have similar request in YouTrack: https://youtrack.jetbrains.com/issue/IDEA-122400. Feel free to vote for it and leave comments.

Best regards, Yaroslav Bedrov JetBrains

EDIT There is at least a way to inspect for Law of Demeter issues. screenshot http://i59.tinypic.com/2s80ft0.png

Here is a gist that contains an inspection profile that will just look for LoD violations. You can import it into IntelliJ.

回答1:

After adding the getFooX() method to Bar, I would use Edit > Find > Replace Structurally with the following expressions:

Search template:

$instance$.getFoo().getX()

Replacement template:

$instance$.getFooX()

It does the job perfectly. Maybe you can add some constraints to $instance$ variable to narrow the search but that would only be useful if you had multiple classess with that method name.