【重構】AndroidStudio中程式碼重構選單Refactor功能詳解
程式碼重構幾乎是每個程式設計師在軟體開發中必須要不斷去做的事情,以此來不斷提高程式碼的質量。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/f8cb51bc