【重構】AndroidStudio中程式碼重構菜單Refactor功能詳解

  • 2019 年 11 月 7 日
  • 筆記

       程式碼重構幾乎是每個程式設計師在軟體開發中必須要不斷去做的事情,以此來不斷提高程式碼的品質。Android Stido(以下簡稱AS)以其強大的功能,成為當下Android開發工程師最受歡迎的開發工具,也是Android官方推薦使用的工具。如此優秀的工具,自然少不了要在程式碼重構這件事情上好好表現一把了。本文將通過程式碼演示,功能截圖來詳細介紹AS為程式碼重構提供的各項功能。

       在AS的主菜單欄中有一項“Refactor”下拉菜單,點擊該下拉菜單,會看到如下的介面,菜單中的每一項,都是為程式碼重構提供的一項自動實現功能。這麼多的功能項,可見AS在程式碼重構功能上的強大,下面我們對這些功能項一一進行介紹。另外,還可以在編輯介面中點擊右鍵,在彈出的菜單中也可以找到“Refactor”。

 

1、Refactor This

       作用:重構當前。操作此項,會顯示對當前游標選中處可行的重構方法。

       示例:選擇了類名“RefactorTest”,操作“Refactor This”後,顯示了可執行的重構方法列表,可以通過選擇數字來執行對應的方法。

       

 

2、Rename

       作用:對游標選中項進行重命名。不僅可以對類中的成員變數進行重命名,還能對文件名,包名等進行重命名,Module中與之相關聯的所有地方都會一起修改,而不用一一手動修改。

       快捷鍵:Shift + F6

       示例:在紅框中輸入修改後的名稱,並按Enter鍵即可。

 

3、Rename File

       作用:修改當前編輯介面顯示的文件的文件名。就相當於滑鼠選中該文件,並執行“Rename”方法。

       示例:在顯示的對話框中輸入新文件名。可以在下方的選項框中選擇修改範圍,引用該文件的地方,注釋,字元串中都可以選擇一起修改。

 

4、Change Signature

       作用:修改方法、類、構造函數的簽名,其實就是修改所選項的一些屬性。

       快捷鍵:Ctr l+ F6

       示例:如下展示了一個方法重構前,重構過程,以及重構後的情形(以修改一個方法簽名為例)。

       重構前:

1 private void testChangeSignature(int first, int second) {  2 }

選中方法名後,執行該重構方法後,會彈出如下對話框,可以對該方法各種屬性進行修改,添加/刪除參數,調整參數順序,新增/刪除異常等。

        重構後:

1 public void testChangeSignature(int second, int first, String third) throws NullPointerException {  2 }

 

5、Type Migration

       作用:類型遷移,即對變數數據類型,或者方法的返回類型進行修改。前面介紹了對文件名,包名,變數名等進行修改,這裡對類型進行修改。

       快捷鍵:Ctrl + Shift + F6

       重構前:

1 private int age = 10;  2 public RefactorTest(int age) {  3     this.age = age;  4 }

選中要修改的類型,執行該重構方法,會彈出對話框,根據需要編輯類型,選中作用範圍即可。指定範圍內,與該變數相關聯處都會被修改。

重構後(由於從int修改到String,所以還需要手動修改變數值): 

1 private String age = "10";  2 public RefactorTest(String age) {  3     this.age = age;  4 }

 

  6、Make Static

       作用:給內部類或者方法添加static關鍵字。示例比較簡單,就不做演示了。

       

  7、Convert To Instance Method

       作用: 轉換為實例方法,即將靜態方法去掉static關鍵字。

 

  8、Move

       功能:移動文件到指定路徑

       快捷鍵:F6

 

9、Copy

       作用:在指定包中拷貝一份當前文件

       快捷鍵:F5

 

10、Safe Detele

       作用:安全刪除,可用於對方法/欄位等進行快速刪除,會刪除掉與之相關聯的引用。

       快捷鍵:Alt + Delete

 

 

11、Extract

   (1)Variable 

       作用:提取變數。這一點在碰到比較長的表達式時經常用到,將看起來很長很複雜的表達式提取出來作為一個變數表示。

       快捷鍵:Ctrl + Alt + V

       重構前:我們常會看到這樣的程式碼

1 public void testExtractVariable() {  2      Log.i("demo", "age=" + getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() + ";name=" + getNnnnnnnnnnnnnnnnnnnnnnname());  3  }  4  private int getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() {  5      return age;  6  }  7  private String getNnnnnnnnnnnnnnnnnnnnnnname() {  8      return name;  9  }

第二行的要列印的資訊表達式太長了,希望單獨提取出來用一個變數表示。本示例中滑鼠停留在第2行“getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge”處,執行該重構方法,會彈出如下紅框部分對話框,顯示的是選中表達式相關的可提取部分,根據需要選擇要提取的部分即可。

重構後: 

 1 public void testExtractVariable() {   2     String msg = "age=" + getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() + ";name=" + getNnnnnnnnnnnnnnnnnnnnnnname();   3     Log.i("demo", msg);   4 }   5 private int getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() {   6     return age;   7 }   8 private String getNnnnnnnnnnnnnnnnnnnnnnname() {   9     return name;  10 }

    (2)Constant

       作用:提取常量,將表達式中的值提取為常量。

       快捷鍵:Ctrl + Alt +C

       重構前:

1 public void testExtractConstant() {  2   String filename = "sdcard";  3 }

重構後:

1 public static final String SDCARD = "sdcard";  2 public void testExtractConstant() {  3   String filename = SDCARD;  4 }

    (3)Filed

       作用:提取欄位,將局部變數提取為全局變數。

       快捷鍵:Ctrl + Alt +F

       重構前:

1 public void testExtractField() {  2    String name ="zhangsan";  3 }

        重構後:

1 private final String string = "zhangsan";  2 public void testExtractField() {  3 }

    (4)Parameter

       作用:將局部變數提取為方法的參數。

       快捷鍵:Ctrl + Alt +P

       重構前:

1 public void testExtractParameter() {  2     printName();  3 }  4 private void printName(){  5     String name = "zhangsan";  6     Log.i("demo","My name is:"+name);  7 }

      重構後:

1 public void testExtractParameter() {  2     printName("zhangsan");  3 }  4 private void printName(String name){  5     Log.i("demo","My name is:"+ name);  6 }

    (5)Functional Parameter ( 函數式參數 ) Ctrl + Alt + Shift + P

    (6)Parameter Object

       作用:將參數提取為一個對象。該功能主要是針對參數比較多的時候,將這些參數提取出來作為一個Bean實例傳入。

       重構前:

1 private void testExtractParamObject() {  2     String info = getInfo("zhangshan", 20, 180f);  3 }  4 private String getInfo(String name, int age, float height) {  5     return "name=" + name + ";age=" + age + ";height=" + height;  6 }

       重構後:

 1 private void testExtractParamObject() {   2     String info = getInfo(new Person("zhangshan", 20, 180f));   3 }   4 private String getInfo(Person person) {   5     return "name=" + person.getName() + ";age=" + person.getAge() + ";height=" + person.getHeight();   6 }   7 private static class Person {   8     private final String name;   9     private final int age;  10     private final float height;  11     private Person(String name, int age, float height) {  12         this.name = name;  13         this.age = age;  14         this.height = height;  15     }  16     public String getName() {  17         return name;  18     }  19     public int getAge() {  20         return age;  21     }  22     public float getHeight() {  23         return height;  24     }  25 }

    (7)Mehtod

       作用:提取為方法

       快捷鍵:Ctrl + Alt +M

       重構前:

1 public void testExtractMethod() {  2     List<String> nameList = new ArrayList<>();  3     nameList.add("zhangshan");  4     nameList.add("lisi");  5     nameList.add("wangwu");  6     int size = nameList.size();  7 }

滑鼠游標選中第2~5行後執行該重構方法

        重構後:

 1 public void testExtractMethod() {   2     List<String> nameList = getNameList();   3     int size = nameList.size();   4 }   5 @NonNull   6 private List<String> getNameList() {   7     List<String> nameList = new ArrayList<>();   8     nameList.add("zhangshan");   9     nameList.add("lisi");  10     nameList.add("wangwu");  11     return nameList;  12 }

    (8)Type Parameter

    (9)Method Object

       作用:將該選中的內容提取為一個方法,並提取到一個獨立的類中。和“Method”很類似,不同的是提取的方法最後放在哪裡。

       重構前:

1 public void testExtractMethod() {  2     List<String> nameList = new ArrayList<>();  3     nameList.add("zhangshan");  4     nameList.add("lisi");  5     nameList.add("wangwu");  6     int size = nameList.size();  7 }

        重構後:

 1 public void testExtractMethod() {   2     List<String> nameList = Utils.invoke();   3     int size = nameList.size();   4 }   5 private static class Utils {   6     private static List<String> invoke() {   7         List<String> nameList = new ArrayList<>();   8         nameList.add("zhangshan");   9         nameList.add("lisi");  10         nameList.add("wangwu");  11         return nameList;  12     }  13 }

    (10)Delegate

       作用:提取為一個代理類。

       重構前:

1 public class RefactorTest{  2  3     public void testExtractInterface() {  4         System.out.print("testExtractInterface");  5     }  6 }

       重構後:

 1 public class RefactorTestDelegate {   2     public RefactorTestDelegate() {   3     }   4   5     public void testExtractInterface() {   6         System.out.print("testExtractInterface");   7     }   8 }   9  10 public class RefactorTest{  11  12     private final RefactorTestDelegate refactorTestDelegate = new RefactorTestDelegate();  13  14     public void testExtractInterface() {  15         refactorTestDelegate.testExtractInterface();  16     }  17 }

    (11)Interrface

       作用:提取為介面。

       重構前:

1 public class RefactorTest {  2  3     public void testExtractInterface() {  4         System.out.print("testExtractInterface");  5     }  6 }

public修飾的方法才可以被提取到介面中。

        重構後: 

 1 interface IRefactorTest {   2     void testExtractInterface();   3 }   4   5 public class RefactorTest implements IRefactorTest {   6   7     @Override   8     public void testExtractInterface() {   9         System.out.print("testExtractInterface");  10     }  11 }

    (12)Superclass

       作用:將指定內容提取到父類中。

       重構前:

1 private void testExtractSupperclass() {  2       testSuper();  3 }  4  5 public void testSuper() {  6      System.out.print("testSuper");  7 }

       重構後:

 1 //=======RefactorTest extends RefactorTestBase=======   2 private void testExtractSupperclass() {   3     testSuper();   4 }   5   6 class RefactorTestBase {   7     public void testSuper() {   8         System.out.print("testSuper");   9     }  10 }

 

12、Inline

       作用:轉換為內聯、方法鏈形式的調用。

       快捷鍵:Ctrl + Alt +N

       重構前:

1 private void testInline() {  2     int a = 100;  3     int b = 200;  4     System.out.print(add(a, b));  5 }  6 private int add(int a, int b) {  7     System.out.print("a=" + a + ";b=" + b);  8     return a*2 + b*3;  9 }

       重構後: 

1 private void testInline() {  2     int a = 100;  3     int b = 200;  4     System.out.print("a=" + a + ";b=" + b);  5     System.out.print(a * 2 + b * 3);  6 }

原先需要調用一個方法,重構後直接把該方法中的程式碼給複製過來了。因為上面選中的是內聯所有的,並且刪除該方法,所以add方法也就被刪除了。

 

13、Find and Replace Code Duplicates

 

14、Invert Boolean

       作用:轉換Boolean值,將當前false/true的值進行轉化為相反的值。

       重構前:

1 private boolean isEmpty(String str) {  2     if (str != null && str.length() == 0) {  3         return false;  4     }  5     return true;  6 }

       重構後:

1 private boolean isNotEmpty(String str) {  2     if (str != null && str.length() == 0) {  3         return true;  4     }  5     return false;  6 }

 

15、Pull Members Up

       作用:將子類的成員上移到父類中。

       重構前:

 1 public class RefactorBase {   2   3 }   4   5 public class RafactorSub extends RefactorBase {   6     int age = 10;   7   8     public void printSub() {   9         System.out.print(age);  10     }  11 }

       重構後:

 1 public class RefactorBase {   2     int age = 10;   3     public void printSub() {   4         System.out.print(age);   5     }   6 }   7   8 public class RafactorSub extends RefactorBase {   9  10 }

 

16、Push Members Down

       作用:將父類中的成員下移到子類中,正好是“Pull Members Up”的反向操作。

       重構前:

 1 public class RefactorBase {   2     int age = 10;   3     public void printSub() {   4         System.out.print(age);   5     }   6 }   7   8 public class RafactorSub extends RefactorBase {   9  10 }

       重構後:

1 public class RefactorBase {  2  3 }  4 public class RafactorSub extends RefactorBase {  5     int age = 10;  6     public void printSub() {  7         System.out.print(age);  8     }  9 }

 

17、Use Interface Where Possible

 

18、Replace Inheritance with Delegation

       作用:使用代理替代繼承。在java中,提倡使用組合,而不是繼承。

       重構前:

 1 public abstract class AbsClass {   2     public abstract void print();   3 }   4   5 public class ClassWrapper extends AbsClass {   6     @Override   7     public void print() {   8         System.out.print("print");   9     }  10 }  11  12 private void testReplaceInheritanceWithDelegation() {  13     new ClassWrapper().print();  14 }

        重構後:

 1 public abstract class AbsClass {   2     public abstract void print();   3 }   4   5 public class ClassWrapper {   6     private final ClassImpl absClass = new ClassImpl();   7   8     public void print() {   9         absClass.print();  10     }  11  12     private class ClassImpl extends AbsClass {  13         @Override  14         public void print() {  15             System.out.print("print");  16         }  17     }  18 }  19  20 public class RefactorTest {  21     private void testReplaceInheritanceWithDelegation() {  22         new ClassWrapper().print();  23     }  24 }

 這一部分有點像Android中Context,ContextWrapper,ContextImpl類之間的關係。

 

19、Remove Middleman

       作用:移除中間人,其實就是移除中間過程。

       重構前:

 1 public class RefactorTest {   2   3     private void testRemoveMiddleMan() {   4         BookManager bookManager = new BookManager();   5         bookManager.addBook("java");   6     }   7   8     public static class BookManager {   9         private List<String> mBookList = new ArrayList<>();  10  11         private void addBook(String bookName) {  12             mBookList.add(bookName);  13         }  14     }  15 }

       重構後:

 1 public class RefactorTest {   2   3     private void testRemoveMiddleMan() {   4         BookManager bookManager = new BookManager();   5         bookManager.getmBookList().add("java");   6     }   7   8     public static class BookManager {   9         private List<String> mBookList = new ArrayList<>();  10  11         public List<String> getmBookList() {  12             return mBookList;  13         }  14     }  15 }

對比重構前和重構後會發現,添加book這個動作,從由BookManager的addBook方法來執行,變成了直接有mBookList來執行了。這個addBook就是這個MiddleMan,顯得多餘,可以優化掉。實際上優化後就變成一個inline方式了,可以對比前面講到的“Inline”。

 

20、Wrap Method Return Value

       作用:封裝返回值

 1 public class RefactorTest {   2   3     private void testWrapReturnValue() {   4         String name = getName();   5     }   6   7     private String getName() {   8         return "zhangsan";   9     }  10 }

       重構後:

 1 public class RefactorTest {   2   3     private void testWrapReturnValue() {   4         String name = getName().getValue();   5     }   6   7     private Person getName() {   8         return new Person("zhangsan");   9     }  10  11     public class Person {  12         private final String value;  13  14         public Person(String value) {  15             this.value = value;  16         }  17  18         public String getValue() {  19             return value;  20         }  21     }  22 }

 

21、Convert Anonymous to Inner

       作用:將匿名內部類轉為內部類。

       重構前:

1 private void testConvertAnonymousToInner(){  2     view.setOnClickListener(new View.OnClickListener() {  3         @Override  4         public void onClick(View v) {  5         }  6     });  7 }

        重構後:

 1 public class RefactorTest{   2   3     View view;   4     private void testConvertAnonymousToInner(){   5         view.setOnClickListener(new MyOnClickListener());   6     }   7   8     private static class MyOnClickListener implements View.OnClickListener {   9         @Override  10         public void onClick(View v) {  11  12         }  13     }  14 }

 

22、Encapsulate Fields

       作用:封裝欄位,用於生成Getter/Setter

       重構前:

1 public String name = "zhangsan";  2 private void testEncapsulateFields() {  3     System.out.println(name);  4 }

 通過該對話框,可以選擇要封裝的欄位,設置修飾符等。默認選擇時,name欄位的修飾符從public變成了private,這也就避免了外部類通過實例直接訪問它。

       重構後:

 1 private String name = "zhangsan";   2 private void testEncapsulateFields() {   3     System.out.println(getName());   4 }   5 public String getName() {   6     return name;   7 }   8 public void setName(String name) {   9     this.name = name;  10 }

 

23、Replace Temp With Query

 

24、Replace Constructor with Factory Method

       作用:將構造方法替換為工廠方法

       重構前:

 1 public class MyClass {   2   3     private String title;   4     private String message;   5     private String sure;   6     private String cancel;   7   8     public MyClass(String title, String message, String sure, String cancel) {   9         this.title = title;  10         this.message = message;  11         this.sure = sure;  12         this.cancel = cancel;  13     }  14 }  15  16 public class RefactorTest {  17     private void testReplaceConstructorWithFactory(Context context) {  18         MyClass myClass = new MyClass("title", "message", "sure", "cancle");  19     }  20 }

       重構後:

 1 public class MyClass {   2   3     private String title;   4     private String message;   5     private String sure;   6     private String cancel;   7   8     private MyClass(String title, String message, String sure, String cancel) {   9         this.title = title;  10         this.message = message;  11         this.sure = sure;  12         this.cancel = cancel;  13     }  14  15     public static MyClass createMyClass(String title, String message, String sure, String cancel) {  16         return new MyClass(title, message, sure, cancel);  17     }  18 }  19  20 public class RefactorTest {  21     private void testReplaceConstructorWithFactory(Context context) {  22         MyClass myClass = MyClass.createMyClass("title", "message", "sure", "cancle");  23     }  24 }

原先public修飾的構造函數,已經變成private了,MyClass類只能通過工廠方法來獲取實例,而無法再直接new了。

 

25、Replace Constructor with Builder

       作用:將構造方法替換為Builder方式

       重構前:

 1 public class RefactorTest{   2     private void testReplaceConstructorWithBuilder(Context context){   3         MyDialog dialog =  new MyDialog(context,"title","message","sure","cancle");   4     }   5 }   6   7 public class MyDialog extends Dialog {   8     private String title;   9     private String message;  10     private String sure;  11     private String cancel;  12     public MyDialog(@NonNull Context context) {  13         super(context);  14     }  15     public MyDialog(Context context, String title, String message, String sure, String cancel) {  16         super(context);  17         this.title = title;  18         this.message = message;  19         this.sure = sure;  20         this.cancel = cancel;  21     }  22 }

 

重構後:

 1 public class RefactorTest {   2     private void testReplaceConstructorWithBuilder(Context context) {   3         MyDialog dialog = new MyDialogBuilder()   4                 .setContext(context)   5                 .setTitle("title")   6                 .setMessage("message")   7                 .setSure("sure")   8                 .setCancel("cancle")   9                 .createMyDialog();  10     }  11 }  12  13 public class MyDialogBuilder {  14     private Context context;  15     private String title;  16     private String message;  17     private String sure;  18     private String cancel;  19  20     public MyDialogBuilder setContext(Context context) {  21         this.context = context;  22         return this;  23     }  24  25     public MyDialogBuilder setTitle(String title) {  26         this.title = title;  27         return this;  28     }  29  30     public MyDialogBuilder setMessage(String message) {  31         this.message = message;  32         return this;  33     }  34  35     public MyDialogBuilder setSure(String sure) {  36         this.sure = sure;  37         return this;  38     }  39  40     public MyDialogBuilder setCancel(String cancel) {  41         this.cancel = cancel;  42         return this;  43     }  44  45     public MyDialog createMyDialog() {  46         return new MyDialog(context);  47     }  48 }

看到這裡,我們應該能夠聯想到AlertDialog類中的Builder了。將構造函數的形式,轉變為了建造者模式的形式,這樣不會拘泥於構造函數的參數個數,參數類型的限制,從而靈活設置屬性。 

 

26、Generify

       作用:泛型重構,自動添加泛型的參數。

       重構前:

1 private void testGenerify() {  2     List list = new ArrayList();  3     list.add("one");  4     list.add("two");  5     list.add("three");  6 }

       重構後:

1 private void testGenerify() {  2     List<String> list = new ArrayList<String>();  3     list.add("one");  4     list.add("two");  5     list.add("three");  6 }

27、Migrate

28、Internationalize(國際化)

 

29、Remove Unused Resources

       作用:一直不用的資源

       示例:下圖中1.jpg是一個沒有被應用的文件。

 

 

在執行該重構方法後,1.jpg就被刪除了。

 

 

參考:https://www.jianshu.com/p/f8cb51bc8e19