代碼重構幾乎是每個程式員在軟體開發中必須要不斷去做的事情,以此來不斷提高代碼的品質。Android Stido(以下簡稱AS)以其強大的功能,成為當下Android開發工程師最受歡迎的開發工具,也是Android官方推薦使用的工具。如此優秀的工具,自然少不了要在代碼重構這件事情上好好表現一把了。本文将通過代碼示範,功能截圖來詳細介紹AS為代碼重構提供的各項功能。
轉載請聲明,轉載自【https://www.cnblogs.com/andy-songwei/p/11809700.html】,謝謝!
代碼重構幾乎是每個程式員在軟體開發中必須要不斷去做的事情,以此來不斷提高代碼的品質。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 }
(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>
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