天天看点

IntelliJ 使用迁移方法(Move Method) 自动化重构时不保留中介方法(Delegate)的问题和解决办法

Move Method 迁移方法是最常用的重构之一。IntelliJ提供了这个自动化重构,快捷键 F6。在使用时会遇到如下问题

Client

package move_method_preserving_delegate;

public class Client {
    void doSomethingWithFoo(Foo foo) {
        foo.calculate();
    }
}
           

Foo

package move_method_preserving_delegate;

public class Foo {
    private Bar _bar;

    int calculate() {
        return _bar.getNumber() + 1;
    }
}
           

Bar

package move_method_preserving_delegate;

public class Bar {
    private int _number;

    int getNumber() {
        return _number;
    }
}
           

Foo的代码里,calculate()的逻辑作用在Bar的属性上,这个坏味使人想把calculate()的逻辑移到Bar上。如果在IntelliJ中用F6键移方法,结果如下

package move_method_preserving_delegate;

public class Client {
    void doSomethingWithFoo(Foo foo) {
        foo._bar.calculate();
    }
}
           
package move_method_preserving_delegate;

public class Foo {
    Bar _bar;

}
           
package move_method_preserving_delegate;

public class Bar {
    private int _number;

    int getNumber() {
        return _number;
    }

    int calculate() {
        return getNumber() + 1;
    }
}
           

问题出现了,Foo的属性bar可见度变成了公开,Foo也没有保留原有的行为。显然IntelliJ的自动化跳过了在原始对象上保留间接方法(Delegate)的这一步重构。解决的方法如下

在F6之前,先把要迁移的方法的逻辑全部代码提取出来,因为不能和原方法同名,所以我加了个后缀_EX,表示是提取出来的

package move_method_preserving_delegate;

public class Foo {
    private Bar _bar;

    int calculate() {
        return calculate_EX();
    }

    private int calculate_EX() {
        return _bar.getNumber() + 1;
    }

}
           

这样就制造了一个中介方法,然后把它用F6移走

package move_method_preserving_delegate;

public class Foo {
    private Bar _bar;

    int calculate() {
        return _bar.calculate_EX();
    }

}
           
package move_method_preserving_delegate;

public class Bar {
    private int _number;

    int getNumber() {
        return _number;
    }

    int calculate_EX() {
        return getNumber() + 1;
    }
}
           

最后重命名回calculate

package move_method_preserving_delegate;

public class Bar {
    private int _number;

    int getNumber() {
        return _number;
    }

    int calculate() {
        return getNumber() + 1;
    }
}
           
package move_method_preserving_delegate;

public class Foo {
    private Bar _bar;

    int calculate() {
        return _bar.calculate();
    }

}
           

这样做,Foo的Client代码就没有发生改变

package move_method_preserving_delegate;

public class Client {
    void doSomethingWithFoo(Foo foo) {
        foo.calculate();
    }
}
           

之后是否要去掉中间人(Remove Middleman)是另一种重构要考虑的了。

***补充***

迁移方法同时保留中介,这步重构的反向操作是融回被迁走的方法

package move_method_preserving_delegate;

public class Bar {
    private int _number;

    int getNumber() {
        return _number;
    }

    int calculate() {
        return getNumber() + 1;
    }
}
           

在Bar上选择calculate(),然后融回(Inline,快捷键Ctrl + Alt + N),就回到使用迁移方法之前的状态了。

package move_method_preserving_delegate;

public class Bar {
    private int _number;

    int getNumber() {
        return _number;
    }

}
           
package move_method_preserving_delegate;

public class Foo {
    private Bar _bar;

    int calculate() {
        return _bar.getNumber() + 1;
    }

}
           

继续阅读