天天看點

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;
    }

}
           

繼續閱讀