【重構】Android Studio在代碼重構中的妙用

       代碼重構幾乎是每一個程序員在軟件開發中必需要不斷去作的事情,以此來不斷提升代碼的質量。Android Stido(如下簡稱AS)以其強大的功能,成爲當下Android開發工程師最受歡迎的開發工具,也是Android官方推薦使用的工具。如此優秀的工具,天然少不了要在代碼重構這件事情上好好表現一把了。本文將經過代碼演示,功能截圖來詳細介紹AS爲代碼重構提供的各項功能。java

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

 

一、Refactor This程序員

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

       示例:選擇了類名「RefactorTest」,操做「Refactor This」後,顯示了可執行的重構方法列表,能夠經過選擇數字來執行對應的方法。app

       

 

二、Renameide

       做用:對光標選中項進行重命名。不只能夠對類中的成員變量進行重命名,還能對文件名,包名等進行重命名,Module中與之相關聯的全部地方都會一塊兒修改,而不用一一手動修改。函數

       快捷鍵:Shift + F6工具

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

 

三、Rename File開發工具

       做用:修改當前編輯界面顯示的文件的文件名。就至關於鼠標選中該文件,並執行「Rename」方法。

       示例:在顯示的對話框中輸入新文件名。能夠在下方的選項框中選擇修改範圍,引用該文件的地方,註釋,字符串中均可以選擇一塊兒修改。

 

四、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 }

 

五、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 }

 

  六、Make Static

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

       

  七、Convert To Instance Method

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

 

  八、Move

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

       快捷鍵:F6

 

九、Copy

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

       快捷鍵:F5

 

十、Safe Detele

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

       快捷鍵:Alt + Delete

 

 

十一、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 }

    (13) Style

       做用:將屬性提取爲Style。該項只有當鼠標停留在佈局文件中時纔會出現。

       重構前:

1 <Button
2     android:id="@+id/btn_handler_demo"
3     android:layout_width="wrap_content"
4     android:layout_height="wrap_content"
5     android:text="handler" />

       

       重構後:

1 <Button
2     android:id="@+id/btn_handler_demo"
3     android:text="handler"
4     style="@style/testStyle" />

 styles.xml

1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3     <style name="testStyle">
4         <item name="android:layout_width">wrap_content</item>
5         <item name="android:layout_height">wrap_content</item>
6     </style>
7 </resources>

    (14)Layout

       做用:提取爲佈局文件。這一項也是須要在鼠標停留在佈局文件中時纔會出現。

 1 <LinearLayout
 2     android:layout_width="match_parent"
 3     android:layout_height="wrap_content"
 4     android:orientation="horizontal">
 5     <Button
 6         android:id="@+id/btn_handler_demo"
 7         android:layout_width="wrap_content"
 8         android:layout_height="wrap_content"
 9         android:text="handler" />
10     <Button
11         android:id="@+id/btn_broadcast_demo"
12         android:layout_width="wrap_content"
13         android:layout_height="wrap_content"
14         android:text="Broadcast" />
15     <Button
16         android:id="@+id/btn_bright_demo"
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:text="Bright" />
20     <Button
21         android:id="@+id/btn_service_demo"
22         android:layout_width="wrap_content"
23         android:layout_height="wrap_content"
24         android:text="Service" />
25 </LinearLayout>

       重構後:

<include layout="@layout/testlayout" />

testlayout.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="wrap_content"
 6     android:orientation="horizontal"
 7     tools:showIn="@layout/activity_main">
 8 
 9     <Button
10         android:id="@+id/btn_preference_demo"
11         android:layout_width="wrap_content"
12         android:layout_height="wrap_content"
13         android:text="Preference" />
14 
15     <Button
16         android:id="@+id/btn_file_demo"
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:text="File" />
20 
21     <Button
22         android:id="@+id/btn_anim_demo"
23         android:layout_width="wrap_content"
24         android:layout_height="wrap_content"
25         android:text="Anim" />
26 
27     <Button
28         android:id="@+id/btn_customview_demo"
29         android:layout_width="wrap_content"
30         android:layout_height="wrap_content"
31         android:text="CustomView" />
32 </LinearLayout>

 

十二、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方法也就被刪除了。

 

1三、Find and Replace Code Duplicates

 

1四、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 }

 

1五、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 }

 

1六、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 }

 

1七、Use Interface Where Possible

 

1八、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類之間的關係。

 

1九、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 }

 

2一、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 }

 

2二、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 }

 

2三、Replace Temp With Query

 

2四、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了。

 

2五、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了。將構造函數的形式,轉變爲了建造者模式的形式,這樣不會拘泥於構造函數的參數個數,參數類型的限制,從而靈活設置屬性。 

 

2六、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 }

2七、Migrate

2八、Internationalize(國際化)

 

2九、Remove Unused Resources

       做用:一直不用的資源

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

 

 

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

 

 

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

相關文章
相關標籤/搜索