Android Studio在代码重构中的妙用

2023-05-25,,

代码重构几乎是每个程序员在软件开发中必须要不断去做的事情,以此来不断提高代码的质量。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

示例:如下展示了一个方法重构前,重构过程,以及重构后的情形(以修改一个方法签名为例)。

重构前:

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

选中方法名后,执行该重构方法后,会弹出如下对话框,可以对该方法各种属性进行修改,添加/删除参数,调整参数顺序,新增/删除异常等。

重构后:

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

5、Type Migration

作用:类型迁移,即对变量数据类型,或者方法的返回类型进行修改。前面介绍了对文件名,包名,变量名等进行修改,这里对类型进行修改。

快捷键:Ctrl + Shift + F6

重构前:

 private int age = 10;
public RefactorTest(int age) {
this.age = age;
}

选中要修改的类型,执行该重构方法,会弹出对话框,根据需要编辑类型,选中作用范围即可。指定范围内,与该变量相关联处都会被修改。

重构后(由于从int修改到String,所以还需要手动修改变量值):

 private String age = "10";
public RefactorTest(String age) {
this.age = age;
}

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

重构前:我们常会看到这样的代码

 public void testExtractVariable() {
Log.i("demo", "age=" + getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() + ";name=" + getNnnnnnnnnnnnnnnnnnnnnnname());
}
private int getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() {
return age;
}
private String getNnnnnnnnnnnnnnnnnnnnnnname() {
return name;
}

第二行的要打印的信息表达式太长了,希望单独提取出来用一个变量表示。本示例中鼠标停留在第2行“getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge”处,执行该重构方法,会弹出如下红框部分对话框,显示的是选中表达式相关的可提取部分,根据需要选择要提取的部分即可。

重构后:

 public void testExtractVariable() {
String msg = "age=" + getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() + ";name=" + getNnnnnnnnnnnnnnnnnnnnnnname();
Log.i("demo", msg);
}
private int getAaaaaaaaaaaaaaaaaaaaaaaaaaaAge() {
return age;
}
private String getNnnnnnnnnnnnnnnnnnnnnnname() {
return name;
}

(2)Constant

作用:提取常量,将表达式中的值提取为常量。

快捷键:Ctrl + Alt +C

重构前:

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

重构后:

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

(3)Filed

作用:提取字段,将局部变量提取为全局变量。

快捷键:Ctrl + Alt +F

重构前:

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

重构后:

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

(4)Parameter

作用:将局部变量提取为方法的参数。

快捷键:Ctrl + Alt +P

重构前:

 public void testExtractParameter() {
printName();
}
private void printName(){
String name = "zhangsan";
Log.i("demo","My name is:"+name);
}

重构后:

 public void testExtractParameter() {
printName("zhangsan");
}
private void printName(String name){
Log.i("demo","My name is:"+ name);
}

(5)Functional Parameter ( 函数式参数 ) Ctrl + Alt + Shift + P

(6)Parameter Object

作用:将参数提取为一个对象。该功能主要是针对参数比较多的时候,将这些参数提取出来作为一个Bean实例传入。

重构前:

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

重构后:

 private void testExtractParamObject() {
String info = getInfo(new Person("zhangshan", 20, 180f));
}
private String getInfo(Person person) {
return "name=" + person.getName() + ";age=" + person.getAge() + ";height=" + person.getHeight();
}
private static class Person {
private final String name;
private final int age;
private final float height;
private Person(String name, int age, float height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public float getHeight() {
return height;
}
}

(7)Mehtod

作用:提取为方法

快捷键:Ctrl + Alt +M

重构前:

 public void testExtractMethod() {
List<String> nameList = new ArrayList<>();
nameList.add("zhangshan");
nameList.add("lisi");
nameList.add("wangwu");
int size = nameList.size();
}

鼠标光标选中第2~5行后执行该重构方法

重构后:

 public void testExtractMethod() {
List<String> nameList = getNameList();
int size = nameList.size();
}
@NonNull
private List<String> getNameList() {
List<String> nameList = new ArrayList<>();
nameList.add("zhangshan");
nameList.add("lisi");
nameList.add("wangwu");
return nameList;
}

(8)Type Parameter

(9)Method Object

作用:将该选中的内容提取为一个方法,并提取到一个独立的类中。和“Method”很类似,不同的是提取的方法最后放在哪里。

重构前:

 public void testExtractMethod() {
List<String> nameList = new ArrayList<>();
nameList.add("zhangshan");
nameList.add("lisi");
nameList.add("wangwu");
int size = nameList.size();
}

重构后:

 public void testExtractMethod() {
List<String> nameList = Utils.invoke();
int size = nameList.size();
}
private static class Utils {
private static List<String> invoke() {
List<String> nameList = new ArrayList<>();
nameList.add("zhangshan");
nameList.add("lisi");
nameList.add("wangwu");
return nameList;
}
}

(10)Delegate

作用:提取为一个代理类。

重构前:

 public class RefactorTest{

     public void testExtractInterface() {
System.out.print("testExtractInterface");
}
}

重构后:

 public class RefactorTestDelegate {
public RefactorTestDelegate() {
} public void testExtractInterface() {
System.out.print("testExtractInterface");
}
} public class RefactorTest{ private final RefactorTestDelegate refactorTestDelegate = new RefactorTestDelegate(); public void testExtractInterface() {
refactorTestDelegate.testExtractInterface();
}
}

(11)Interrface

作用:提取为接口。

重构前:

 public class RefactorTest {

     public void testExtractInterface() {
System.out.print("testExtractInterface");
}
}

public修饰的方法才可以被提取到接口中。

重构后:

 interface IRefactorTest {
void testExtractInterface();
} public class RefactorTest implements IRefactorTest { @Override
public void testExtractInterface() {
System.out.print("testExtractInterface");
}
}

(12)Superclass

作用:将指定内容提取到父类中。

重构前:

 private void testExtractSupperclass() {
testSuper();
} public void testSuper() {
System.out.print("testSuper");
}

重构后:

 //=======RefactorTest extends RefactorTestBase=======
private void testExtractSupperclass() {
testSuper();
} class RefactorTestBase {
public void testSuper() {
System.out.print("testSuper");
}
}

(13) Style

作用:将属性提取为Style。该项只有当鼠标停留在布局文件中时才会出现。

重构前:

 <Button
android:id="@+id/btn_handler_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="handler" />

重构后:

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

styles.xml

 <?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="testStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
</resources>

(14)Layout

作用:提取为布局文件。这一项也是需要在鼠标停留在布局文件中时才会出现。

 <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_handler_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="handler" />
<Button
android:id="@+id/btn_broadcast_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Broadcast" />
<Button
android:id="@+id/btn_bright_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bright" />
<Button
android:id="@+id/btn_service_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Service" />
</LinearLayout>

重构后:

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

testlayout.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:showIn="@layout/activity_main"> <Button
android:id="@+id/btn_preference_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Preference" /> <Button
android:id="@+id/btn_file_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="File" /> <Button
android:id="@+id/btn_anim_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Anim" /> <Button
android:id="@+id/btn_customview_demo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CustomView" />
</LinearLayout>

12、Inline

作用:转换为内联、方法链形式的调用。

快捷键:Ctrl + Alt +N

重构前:

 private void testInline() {
int a = 100;
int b = 200;
System.out.print(add(a, b));
}
private int add(int a, int b) {
System.out.print("a=" + a + ";b=" + b);
return a*2 + b*3;
}

重构后:

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

原先需要调用一个方法,重构后直接把该方法中的代码给复制过来了。因为上面选中的是内联所有的,并且删除该方法,所以add方法也就被删除了。

13、Find and Replace Code Duplicates

14、Invert Boolean

作用:转换Boolean值,将当前false/true的值进行转化为相反的值。

重构前:

 private boolean isEmpty(String str) {
if (str != null && str.length() == 0) {
return false;
}
return true;
}

重构后:

 private boolean isNotEmpty(String str) {
if (str != null && str.length() == 0) {
return true;
}
return false;
}

15、Pull Members Up

作用:将子类的成员上移到父类中。

重构前:

 public class RefactorBase {

 }

 public class RafactorSub extends RefactorBase {
int age = 10; public void printSub() {
System.out.print(age);
}
}

重构后:

 public class RefactorBase {
int age = 10;
public void printSub() {
System.out.print(age);
}
} public class RafactorSub extends RefactorBase { }

16、Push Members Down

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

重构前:

 public class RefactorBase {
int age = 10;
public void printSub() {
System.out.print(age);
}
} public class RafactorSub extends RefactorBase { }

重构后:

 public class RefactorBase {

 }
public class RafactorSub extends RefactorBase {
int age = 10;
public void printSub() {
System.out.print(age);
}
}

17、Use Interface Where Possible

18、Replace Inheritance with Delegation

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

重构前:

 public abstract class AbsClass {
public abstract void print();
} public class ClassWrapper extends AbsClass {
@Override
public void print() {
System.out.print("print");
}
} private void testReplaceInheritanceWithDelegation() {
new ClassWrapper().print();
}

重构后:

 public abstract class AbsClass {
public abstract void print();
} public class ClassWrapper {
private final ClassImpl absClass = new ClassImpl(); public void print() {
absClass.print();
} private class ClassImpl extends AbsClass {
@Override
public void print() {
System.out.print("print");
}
}
} public class RefactorTest {
private void testReplaceInheritanceWithDelegation() {
new ClassWrapper().print();
}
}

这一部分有点像Android中Context,ContextWrapper,ContextImpl类之间的关系。

19、Remove Middleman

作用:移除中间人,其实就是移除中间过程。

重构前:

 public class RefactorTest {

     private void testRemoveMiddleMan() {
BookManager bookManager = new BookManager();
bookManager.addBook("java");
} public static class BookManager {
private List<String> mBookList = new ArrayList<>(); private void addBook(String bookName) {
mBookList.add(bookName);
}
}
}

重构后:

 public class RefactorTest {

     private void testRemoveMiddleMan() {
BookManager bookManager = new BookManager();
bookManager.getmBookList().add("java");
} public static class BookManager {
private List<String> mBookList = new ArrayList<>(); public List<String> getmBookList() {
return mBookList;
}
}
}

对比重构前和重构后会发现,添加book这个动作,从由BookManager的addBook方法来执行,变成了直接有mBookList来执行了。这个addBook就是这个MiddleMan,显得多余,可以优化掉。实际上优化后就变成一个inline方式了,可以对比前面讲到的“Inline”。

20、Wrap Method Return Value

作用:封装返回值

 public class RefactorTest {

     private void testWrapReturnValue() {
String name = getName();
} private String getName() {
return "zhangsan";
}
}

重构后:

 public class RefactorTest {

     private void testWrapReturnValue() {
String name = getName().getValue();
} private Person getName() {
return new Person("zhangsan");
} public class Person {
private final String value; public Person(String value) {
this.value = value;
} public String getValue() {
return value;
}
}
}

21、Convert Anonymous to Inner

作用:将匿名内部类转为内部类。

重构前:

 private void testConvertAnonymousToInner(){
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}

重构后:

 public class RefactorTest{

     View view;
private void testConvertAnonymousToInner(){
view.setOnClickListener(new MyOnClickListener());
} private static class MyOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) { }
}
}

22、Encapsulate Fields

作用:封装字段,用于生成Getter/Setter

重构前:

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

通过该对话框,可以选择要封装的字段,设置修饰符等。默认选择时,name字段的修饰符从public变成了private,这也就避免了外部类通过实例直接访问它。

重构后:

 private String name = "zhangsan";
private void testEncapsulateFields() {
System.out.println(getName());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

23、Replace Temp With Query

24、Replace Constructor with Factory Method

作用:将构造方法替换为工厂方法

重构前:

 public class MyClass {

     private String title;
private String message;
private String sure;
private String cancel; public MyClass(String title, String message, String sure, String cancel) {
this.title = title;
this.message = message;
this.sure = sure;
this.cancel = cancel;
}
} public class RefactorTest {
private void testReplaceConstructorWithFactory(Context context) {
MyClass myClass = new MyClass("title", "message", "sure", "cancle");
}
}

重构后:

 public class MyClass {

     private String title;
private String message;
private String sure;
private String cancel; private MyClass(String title, String message, String sure, String cancel) {
this.title = title;
this.message = message;
this.sure = sure;
this.cancel = cancel;
} public static MyClass createMyClass(String title, String message, String sure, String cancel) {
return new MyClass(title, message, sure, cancel);
}
} public class RefactorTest {
private void testReplaceConstructorWithFactory(Context context) {
MyClass myClass = MyClass.createMyClass("title", "message", "sure", "cancle");
}
}

原先public修饰的构造函数,已经变成private了,MyClass类只能通过工厂方法来获取实例,而无法再直接new了。

25、Replace Constructor with Builder

作用:将构造方法替换为Builder方式

重构前:

 public class RefactorTest{
private void testReplaceConstructorWithBuilder(Context context){
MyDialog dialog = new MyDialog(context,"title","message","sure","cancle");
}
} public class MyDialog extends Dialog {
private String title;
private String message;
private String sure;
private String cancel;
public MyDialog(@NonNull Context context) {
super(context);
}
public MyDialog(Context context, String title, String message, String sure, String cancel) {
super(context);
this.title = title;
this.message = message;
this.sure = sure;
this.cancel = cancel;
}
}

重构后:

 public class RefactorTest {
private void testReplaceConstructorWithBuilder(Context context) {
MyDialog dialog = new MyDialogBuilder()
.setContext(context)
.setTitle("title")
.setMessage("message")
.setSure("sure")
.setCancel("cancle")
.createMyDialog();
}
} public class MyDialogBuilder {
private Context context;
private String title;
private String message;
private String sure;
private String cancel; public MyDialogBuilder setContext(Context context) {
this.context = context;
return this;
} public MyDialogBuilder setTitle(String title) {
this.title = title;
return this;
} public MyDialogBuilder setMessage(String message) {
this.message = message;
return this;
} public MyDialogBuilder setSure(String sure) {
this.sure = sure;
return this;
} public MyDialogBuilder setCancel(String cancel) {
this.cancel = cancel;
return this;
} public MyDialog createMyDialog() {
return new MyDialog(context);
}
}

看到这里,我们应该能够联想到AlertDialog类中的Builder了。将构造函数的形式,转变为了建造者模式的形式,这样不会拘泥于构造函数的参数个数,参数类型的限制,从而灵活设置属性。

26、Generify

作用:泛型重构,自动添加泛型的参数。

重构前:

 private void testGenerify() {
List list = new ArrayList();
list.add("one");
list.add("two");
list.add("three");
}

重构后:

 private void testGenerify() {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
}

27、Migrate

28、Internationalize(国际化)

29、Remove Unused Resources

作用:一直不用的资源

示例:下图中1.jpg是一个没有被应用的文件。

在执行该重构方法后,1.jpg就被删除了。

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

出处:https://www.cnblogs.com/andy-songwei/p/11809700.html

====================================================================================================================

下面我把简书https://www.jianshu.com/p/f8cb51bc8e19上的内容也贴进来了,一起参考吧!

使用Android Studio对代码进行重构


简介

2013年Google I/O大会上,谷歌推出新的Android开发环境——Android Studio,从此Android程序员有了新的选择,使用Android Studio进行App开发。AndroidStudio是一项全新的基于IntelliJ IDEA的Android开发环境。类似于Eclipse ADT插件,现在已经成为了官方推荐的ide,同时Eclipse不再进行更新。

软件开发中,经过几个版本迭代,不少程序员会觉的以前的代码架构可能不满足日益增长的需求,这时候都会想到了重构,关于重构网上也有不少用于的资料。

重构资料

资料里面列举了如下几种方法,对代码进行重构。

31天重构学习笔记01. 封装集合
31天重构学习笔记02. 移动方法
31天重构学习笔记03. 提升方法
31天重构学习笔记04. 降低方法
31天重构学习笔记05. 提升字段
31天重构学习笔记06. 降低字段
31天重构学习笔记07. 重命名(方法,类,参数)
31天重构学习笔记08. 使用委派代替继承
31天重构学习笔记09. 提取接口
31天重构学习笔记10. 提取方法
31天重构学习笔记11. 使用策略类
31天重构学习笔记12. 分解依赖
31天重构学习笔记13. 提取方法对象
31天重构学习笔记14. 分离职责
31天重构学习笔记15. 移除重复内容
31天重构学习笔记16. 封装条件
31天重构学习笔记17. 提取父类
31天重构学习笔记18. 使用条件判断代替异常
31天重构学习笔记19. 提取工厂类
31天重构学习笔记20. 提取子类
31天重构学习笔记21. 合并继承
31天重构学习笔记22. 分解方法
31天重构学习笔记23. 引入参数对象
31天重构学习笔记24. 分解复杂判断
31天重构学习笔记25. 引入契约式设计
31天重构学习笔记26. 避免双重否定
31天重构学习笔记27. 去除上帝类
31天重构学习笔记28. 为布尔方法命名
31天重构学习笔记29. 去除中间人对象
31天重构学习笔记30. 尽快返回
31天重构学习笔记31. 使用多态代替条件判断

Android Studio是基于优秀的ide的,ide提供了丰富的功能,很方便的对代码进行重构,下图是我的Android Studio Refactor菜单,部分快捷键与默认快捷键不同是因为方便而修改的。

Android Studio Refactor

古人云『工欲善其事必先利其器』,所以在对代码进行重构前,需要详细了解Android Studio提供的功能,下面对AS(Android Studio,后面简称AS)菜单进行简单示例。

如何使用

鼠标光标选中或者放在代码上,按下快捷键就可以弹出当前代码可以使用的功能。
如下所示:

如何使用

代码与操作示例

ChangeSignature,改变函数签名,可以修改函数的名字,参数的顺序,参数的名字。

Before:

    // 改变签名
void testChangeSignature(int second, int first) {
System.out.println(first + "->" + second);
}

Gif:

改变签名

After:

    // 改变签名
void testChangeSignature(int one, int two) {
System.out.println(one + "->" + two);
}

修改前参数依次是second、first,修改是one、two。

ChangeClassSignatuere,改变类的签名,可以修改类的泛型签名

Before:

    // 改变类的签名
void testChangeClassSignatuere() {
String second = "second";
int first = 100;
new ChangeClassSignatuere(second, first);
} // **********************分割线******************************* public class ChangeClassSignatuere { private int first;
private String second; public ChangeClassSignatuere(String second, int first) {
this.first = first;
this.second = second;
} @Override
public String toString() {
return "ChangeClassSignatuere{" +
"second='" + second + '\'' +
",first=" + first +
'}';
}
}

Gif:

改变类签名

After:

    // 改变类的签名
void testChangeClassSignatuere() {
String second = "second";
int first = 100;
new ChangeClassSignatuere<Activity>(second, first);
}
// **********************分割线******************************* public class ChangeClassSignatuere<A> { private int first;
private String second; public ChangeClassSignatuere(String second, int first) {
this.first = first;
this.second = second;
} @Override
public String toString() {
return "ChangeClassSignatuere{" +
"second='" + second + '\'' +
",first=" + first +
'}';
}
}

修改匿名类为内部类

Before:

    // 匿名类改成内部类
void testConvertAnonymousToInner() {
View.OnClickListener clickListener = new View.OnClickListener() { @Override
public void onClick(View v) {
System.out.println("onClick");
}
};
}

Gif:

改变类签名

After:

    // 匿名类改成内部类
void testConvertAnonymousToInner() { View.OnClickListener clickListener = new Abc123();
} private static class Abc123 implements View.OnClickListener { @Override
public void onClick(View v) {
System.out.println("onClick");
}
}

这个是我最喜欢的一个功能,有时候匿名类刚刚开始逻辑很简单,过不了多久逻辑太多,就可以使用快捷键变成一个内部类。

ConvertToInstanceMethod,修改一个方法变成成员方法

Befor:

    // 变成成员方法
void testConvertToInstanceMethod() {
TestClass.convertToInstanceMethod(this, "test");
} public class TestClass { static void convertToInstanceMethod(RefactorDemo demo, String string) {
System.out.println("convertToInstanceMethod = " + demo);
System.out.println("convertToInstanceMethod = " + string);
}
}

After:

    // 变成成员方法
void testConvertToInstanceMethod() {
this.convertToInstanceMethod("test");
} void convertToInstanceMethod(String string) {
System.out.println("convertToInstanceMethod = " + this);
System.out.println("convertToInstanceMethod = " + string);
}

在修改前,调用convertToInstanceMethod方法,第一个参数相是RefactorDemo,传递是this, 修改后就那个方法直接移动到本类中。

Copy,复制一个类

Before:

    // 复制一个类
void testCopy() {
new FirstClass();
} public class FirstClass implements Serializable { public String first; @Override
public String toString() {
return "FirstClass{" +
"first='" + first + '\'' +
'}';
}
}

Gif:

复制一个类

After:

public class SecondClass implements Serializable {

    public String first;

    @Override
public String toString() {
return "SecondClass{" +
"first='" + first + '\'' +
'}';
}
}

EncapsulateFields,压缩一个字段,间接访问

Before:

    String filed = "filed";

    // 压缩一个字段,间接访问
void testEncapsulateFields() {
System.out.println(filed);
}

After:

    private String filed = "filed";

    // 压缩一个字段,间接访问
void testEncapsulateFields() {
System.out.println(getFiled());
} public String getFiled() {
return filed;
} public void setFiled(String filed) {
this.filed = filed;
}

相当于简介访问一个field,自动生成setter和getter。

GenerifyRefactoring,泛型重构

Before:

    // 泛型重构
void testGenerifyRefactoring() {
List list = new ArrayList();
list.add("one");
list.add("two");
list.add("three");
}

After:

    // 泛型重构
void testGenerifyRefactoring() {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
}

自动添加泛型的参数。

Inline,内联函数

Before:

    // 内联
void testInline() {
int a = 100;
int b = 200;
System.out.println(add(a, b));
} int add(int a, int b) {
return a + b;
}

Gif:

 

After:

    // 内联
void testInline() {
int a = 100;
int b = 200;
System.out.println(a + b);
}

原先需要调用一个函数的地方,直接把那个函数里面的代码复制过来。

InvertBoolean,重构Boolean

Before:

    // 重构Boolean
void testInvertBoolean() {
System.out.println(checkPaswd(null));
System.out.println(checkPaswd(""));
System.out.println(checkPaswd("admin"));
} boolean checkPaswd(String passwd) {
if (passwd != null && passwd.length() != 0) {
return true;
}
return false;
}

Gif:

 

After:

    // 重构Boolean
void testInvertBoolean() {
System.out.println(checkPaswd(null));
System.out.println(checkPaswd(""));
System.out.println(checkPaswd("admin"));
} boolean checkPaswd(String passwd) {
return passwd != null && passwd.length() != 0;
}

MakeClassStatic,使类变成静态的

Before:

    // 使类变成静态的
void testMakeClassStatic() {
new MyClass().fun();
} String myClassField = "abc"; class MyClass {
void fun() {
System.out.println("myClassField = " + myClassField);
}
}

Gif:

 

After:

    // 使类变成静态的
void testMakeClassStatic() {
new MyClass(myClassField).fun();
} String myClassField = "abc"; static class MyClass {
private String myClassField; public MyClass(String myClassField) {
this.myClassField = myClassField;
} void fun() {
System.out.println("myClassField = " + myClassField);
}
}

修改前需要调用外部内的成员变量myClassField,实际编译过后MyClass的class是持有外部类的对象,这样才能访问myClassField,通过重构,使得myClassField通过构造方法传入进去,然后再在fun方法中使用,这样重构会减少类的依赖。

MakeMethodStatic,使方法变成静态的

Before:

    // 使方法变成静态的
void testMakeMethodStatic() {
myFun();
} void myFun() {
System.out.println("myFun");
}

After:

    // 使方法变成静态的
void testMakeMethodStatic() {
myFun();
} static void myFun() {
System.out.println("myFun");
}

Move,移动一个类

Before:

    // 移动一个类
void testMove() {
new MoveClass().fun();
} static class MoveClass { String movde; @Override
public String toString() {
return "MoveClass{" +
"movde='" + movde + '\'' +
'}';
} void fun() {
System.out.println(this);
}
}

Gif:

 

After:

    // 移动一个类
void testMove() {
new MoveClassxxxxxx().fun();
} class MoveClassxxxxxx { String movde; @Override
public String toString() {
return "MoveClass{" +
"movde='" + movde + '\'' +
'}';
} void fun() {
System.out.println(this);
}
}

PullMenmberUp,上移

Before:

public class Base {

    public String baseString;

    public Base(String baseString) {
this.baseString = baseString;
} public void funBase() {
System.out.println(baseString);
}
}
public class Sub extends Base { public Sub(String baseString) {
super(baseString);
} public void funSub() {
System.out.println(baseString);
}
} // 上移
void testPullMenmberUp() {
new Sub("base").funSub();
}

Gif:

 

After:

public class Base {

    public String baseString;

    public Base(String baseString) {
this.baseString = baseString;
} public void funBase() {
System.out.println(baseString);
} public void funSub() {
System.out.println(baseString);
}
} public class Sub extends Base { public Sub(String baseString) {
super(baseString);
} }

实际上就是把子类中的方法移动到父类中。

PullMenmberDown,下移

Before:

public class Base {

    public String baseString;

    public Base(String baseString) {
this.baseString = baseString;
} public void funBase() {
System.out.println(baseString);
}
}
public class Sub extends Base { public Sub(String baseString) {
super(baseString);
} public void funSub() {
System.out.println(baseString);
}
}
// 下移
void testPullMenmberDown() {
new Sub("base").funBase();
}

After:

public class Base {

    public String baseString;

    public Base(String baseString) {
this.baseString = baseString;
} } public class Sub extends Base { public Sub(String baseString) {
super(baseString);
} public void funSub() {
System.out.println(baseString);
} public void funBase() {
System.out.println(baseString);
}
}

实际上就是把父类中的方法移动到子类中。

Rename,重命名

这个功能也是我最喜欢的,只要选择重命名一个对象或者资源,所有使用到这个对象或者资源的地方,都会进行重新命名。

Before:

    // 重命名
void testRename() {
String first = "second";
System.out.println(first);
}

Gif:

 

After:

    // 重命名
void testRename() {
String second = "second";
System.out.println(second);
}

ReplaceConstructorWithBuilder,构造方法变成builder

还在羡慕Picasso,Fresco人性化调用方式吗?很简单,通过这个功能就可以快速生成代码

Before:

public class MyAlertDialog {

    private String title;
private String message;
private String okButton;
private String cancelButton; public MyAlertDialog(String title, String message, String okButton, String cancelButton) {
this.title = title;
this.message = message;
this.okButton = okButton;
this.cancelButton = cancelButton;
}
}
// 构造方法变成builder
void testReplaceConstructorWithBuilder() {
new MyAlertDialog("title", "message", "ok", "cancel").show();
}

Gif:

 

After:

    // 构造方法变成builder
void testReplaceConstructorWithBuilder() {
new MyAlertDialog.Builder()
.setTitle("title")
.setMessage("message")
.setOkButton("ok")
.setCancelButton("cancel")
.createMyAlertDialog()
.show();
} public static class Builder {
private String title;
private String message;
private String okButton;
private String cancelButton; public Builder setTitle(String title) {
this.title = title;
return this;
} public Builder setMessage(String message) {
this.message = message;
return this;
} public Builder setOkButton(String okButton) {
this.okButton = okButton;
return this;
} public Builder setCancelButton(String cancelButton) {
this.cancelButton = cancelButton;
return this;
} public MyAlertDialog createMyAlertDialog() {
return new MyAlertDialog(title, message, okButton, cancelButton);
}
}

ReplaceConstructorWithFactory,构造方法变成工程方法

Before:

    // 构造方法变成工程方法
void testReplaceConstructorWithFactory() {
new MyAlertDialog("title", "message", "ok", "cancel").show();
}

Gif:

 

After:

    // 构造方法变成工程方法
void testReplaceConstructorWithFactory() {
MyAlertDialog.newInstance("title", "message", "ok", "cancel")
.show();
}
public class MyAlertDialog {
private MyAlertDialog(String title, String message, String okButton, String cancelButton) {
this.title = title;
this.message = message;
this.okButton = okButton;
this.cancelButton = cancelButton;
} public static MyAlertDialog newInstance(String title, String message, String okButton, String cancelButton) {
return new MyAlertDialog(title, message, okButton, cancelButton);
}
}

通过上面代码发现,如果构造方法变成了工厂方式,那么它的构造参数是private的,这样调用者只能通过工厂方式来生成对象。

ReplaceInheritanceWithDelegation,代理代替继承

Before:

public abstract class AbsClass {

    public abstract void sayHello();
} public class ExtClass extends AbsClass { @Override
public void sayHello() {
System.out.println("hello");
}
}
// 代理代替继承
void testReplaceInheritanceWithDelegation() {
new ExtClass().sayHello();
}

Gif:

 

After:

public class ExtClass {

    private final AbsClassImpl abs = new AbsClassImpl();

    public void sayHello() {
abs.sayHello();
} private class AbsClassImpl extends AbsClass {
@Override
public void sayHello() {
System.out.println("hello");
}
}
} // 代理代替继承
void testReplaceInheritanceWithDelegation() {
new ExtClass().sayHello();
}

书上说过,组合由于继承大概说的就是这个意思。

SafeDelete,安全删除

Before:

    // 安全删除
void testSafeDelete() {
String unUsedString = "abc"; unUsedString = getUnUsedString();
System.out.println(new Date());
} public String getUnUsedString() {
return unUsedString;
}

After:

    // 安全删除
void testSafeDelete() {
System.out.println(new Date());
}

这个比较简单,如果某个变量没有使用,那么直接直接删除所有引用这个变量的地方。

WrapReturnValue,封装返回值

Before:

    // 封装返回值
void testWrapReturnValue() {
System.out.println(getUserName());
} private String getUserName() {
return "userName";
}

Gif:

 

After:

    // 包装下返回值
void testWrapReturnValue() {
System.out.println(getUserName().getValue());
} private UserInfo getUserName() {
return new UserInfo("userName");
} public class UserInfo {
private final String value; public UserInfo(String value) {
this.value = value;
} public String getValue() {
return value;
}
}

RemoveMiddleMan,去除中间人

Before:

    // 去除中间人
void testRemoveMiddleMan() {
OrderManager manager = new OrderManager();
manager.newOrder("new");
manager.confirm("confir");
manager.invalidOrder("invalid");
} public static class OrderManager {
private List<String> unProcessOrderList;
private List<String> processedOrderList; public OrderManager() {
unProcessOrderList = new ArrayList<>();
processedOrderList = new ArrayList<>();
} public void newOrder(String order) {
unProcessOrderList.add(order);
} public void confirm(String order) {
unProcessOrderList.remove(order);
processedOrderList.add(order);
} public void invalidOrder(String order) {
processedOrderList.remove(order);
} public void display() {
System.out.println(unProcessOrderList);
System.out.println(processedOrderList);
}
}

Gif:

 

After:

    // 去除中间人
void testRemoveMiddleMan() {
OrderManager manager = new OrderManager();
manager.getUnProcessOrderList().add("new");
manager.confirm("confir");
manager.getProcessedOrderList().remove("invalid");
} public static class OrderManager {
private List<String> unProcessOrderList;
private List<String> processedOrderList; public OrderManager() {
unProcessOrderList = new ArrayList<>();
processedOrderList = new ArrayList<>();
} public void confirm(String order) {
unProcessOrderList.remove(order);
processedOrderList.add(order);
} public void display() {
System.out.println(unProcessOrderList);
System.out.println(processedOrderList);
} public List<String> getUnProcessOrderList() {
return unProcessOrderList;
} public List<String> getProcessedOrderList() {
return processedOrderList;
}
}

用途就是,原先OrderManager持有一个List来保存Order,调用方直接使用OrderManager来处理订单,去除中间人的意思就是说,调用法直接获取保存Order的容器,调用方直接自己控制容器。

代码抽取

代码抽取在实际使用当中非常有用。

ExtraMethod,抽取一个方法

Before:

    // 抽取一个方法
void testExtraMethod() {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 2); System.out.println(map);
}

Gif:

 

After:

    // 抽取一个方法
void testExtraMethod() {
Map<String, Integer> map = initMap(); System.out.println(map);
} @NonNull
private Map<String, Integer> initMap() {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 2);
return map;
}

ExtraMethodObject,抽取一个方法到一个对象中

和上面那个差不多,只不过把方法移动到另外一个类中。

Before:

    // 抽取一个方法到一个对象中
void testExtraMethodObject() {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 22);
map.put("three", 33); System.out.println(map);
}

After:

    // 抽取一个方法到一个对象中
void testExtraMethodObject() {
Map<String, Integer> map = MapUtil.invoke();
System.out.println(map);
} private static class MapUtil {
private static Map<String, Integer> invoke() {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 22);
map.put("three", 33);
return map;
}
}

ExtractParameterObject,抽取若干参数成一个类

这个比较使用,有时候一个方法参数太多,可以把这些参数合并成一个类。

Before:

    // 抽取若干参数成一个类
void testExtractParameterObject() {
print(100, 200);
} // widht,height --> Size Class
void print(int width, int height) {
System.out.println("width = " + width + ", height = " + height);
}

Gif:

 

After:

    // 抽取若干参数成一个类
void testExtractParameterObject() {
print(new Size(100, 200));
} // widht,height --> Size Class
void print(Size size) {
System.out.println("width = " + size.getWidth() + ", height = " + size.getHeight());
} public class Size {
private final int width;
private final int height; public Size(int width, int height) {
this.width = width;
this.height = height;
} public int getWidth() {
return width;
} public int getHeight() {
return height;
}
}

ExtractSuperclass,抽取到父类

Before:

    // 抽取到父类
void testExtractSuperclass() {
sendEvent("login_success");
} public void sendEvent(Object event) {
// EventBus.getDefault().send(event);
}

Gif:

 

After:

public class BaseExtractDemo {
public void sendEvent(Object event) {
// EventBus.getDefault().send(event);
}
} public class ExtractDemo extends BaseExtractDemo {
// 抽取到父类
void testExtractSuperclass() {
sendEvent("login_success");
}
}

ExtractConstant,抽取常量

Before:

    // 抽取常量
void testExtractConstant() {
String email = "admin@123.com";
String passwd = "1qaz2wsx"; Bundle bundle = new Bundle();
bundle.putString("email", email);
bundle.putString("passwd", passwd);
}

Gif:

 

After:

    public static final String KEY_EMAIL = "email";
public static final String KEY_PASSWD = "passwd"; // 抽取常量
void testExtractConstant() {
String email = "admin@123.com";
String passwd = "1qaz2wsx"; Bundle bundle = new Bundle();
bundle.putString(KEY_EMAIL, email);
bundle.putString(KEY_PASSWD, passwd);
}

ExtractField,抽取成成员变量

Before:

    // 抽取成成员变量
void testExtractField() {
String testExtractField = "testExtractField";
System.out.println(testExtractField);
}

After:

    private String testExtractField;

    // 抽取成成员变量
void testExtractField() {
testExtractField = "testExtractField";
System.out.println(testExtractField);
}

ExtractVariable,抽取成变量

Before:

    // 抽取成变量
void testExtractVariable() {
System.out.println("long name = " + getLoooooooooooooooooooooooooooooooooooooooooooooooooooooongName());
} String getLoooooooooooooooooooooooooooooooooooooooooooooooooooooongName() {
return "getLoooooooooooooooooooooooooooooooooooooooooooooooooooooongName";
}

Gif:

 

After:

    // 抽取成变量
void testExtractVariable() {
String name = getLoooooooooooooooooooooooooooooooooooooooooooooooooooooongName();
System.out.println("long name = " + name);
} String getLoooooooooooooooooooooooooooooooooooooooooooooooooooooongName() {
return "getLoooooooooooooooooooooooooooooooooooooooooooooooooooooongName";
}

有时候,一行代码写的很长,可以使用这个方法,对代码进行重构。

ExtractParameter,抽取成方法的参数

Before:

    // 抽取成方法的参数
void testExtractParameter() {
printHelloString();
} private void printHelloString() {
String str = "printHello";
System.out.println(str);
}

Gif:

 

After:

    // 抽取成方法的参数
void testExtractParameter() {
String str = "printHello";
printStringAndLength(str);
} private void printStringAndLength(String paramStr) {
System.out.println(paramStr + " -> " + paramStr.length());
}

Android资源重构

ExtractLayout,布局文件抽取

Before:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main" /> <android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" /> </android.support.design.widget.CoordinatorLayout>

Gif:

 

After:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"> <include layout="@layout/view_top" /> <include layout="@layout/content_main" /> <include layout="@layout/view_button" /> </android.support.design.widget.CoordinatorLayout>

ExtractStyle,样式抽取

Before:

    <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textAlignment="center"
android:textColor="#f00ff0"
android:textSize="18sp"
android:textStyle="bold"
android:typeface="normal" />

Gif:

 

After:

    <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
style="@style/my_textview_style" /> <style name="my_textview_style">
<item name="android:textAlignment">center</item>
<item name="android:textColor">#f00ff0</item>
<item name="android:textSize">18sp</item>
<item name="android:textStyle">bold</item>
<item name="android:typeface">normal</item>
</style>

综合示例

通过一个示例演示怎么样重构代码,示例是一个Activity里面有个RecyclerView,然后重构代码,演示怎么样分离Adapter,ViewHolder等。

Before:

public class StartActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this)); final List<String> data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
data.add("Text " + (i + 1));
} recyclerView.setAdapter(new RecyclerView.Adapter<MyViewHolder>() { @Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return new MyViewHolder(layoutInflater.inflate(android.R.layout.simple_list_item_1, parent, false));
} @Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.bind(data.get(position));
} @Override
public int getItemCount() {
return data.size();
}
});
} class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; public MyViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(android.R.id.text1);
} void bind(String text) {
textView.setText(text);
}
}
}

Gif:

 

After:

public class StartActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
private List<String> data; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); initData();
initView();
} private void initData() {
data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
data.add("Text " + (i + 1));
}
} private void initView() {
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MyViewHolderAdapter(data));
}
} class MyViewHolderAdapter extends RecyclerView.Adapter<MyViewHolder> { private List<String> data; public MyViewHolderAdapter(List<String> data) {
this.data = data;
} @Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return MyViewHolder.newInstance(parent);
} @Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.bind(data.get(position));
} @Override
public int getItemCount() {
return data.size();
}
} class MyViewHolder extends RecyclerView.ViewHolder { public static final int LAYOUT_ID = android.R.layout.simple_list_item_1; @NonNull
static MyViewHolder newInstance(ViewGroup parent) {
Context context = parent.getContext();
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return new MyViewHolder(layoutInflater.inflate(LAYOUT_ID, parent, false));
} TextView textView; public MyViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(android.R.id.text1);
} void bind(String text) {
textView.setText(text);
}
}

总结

至此,Android Studio的重构功能讲解完成,由于Android Studio更新很快,很多功能还需要自己去发掘。

『君子生非异也,善假于物也』,好好使用工具,可以偷个懒。

出处:https://www.jianshu.com/p/f8cb51bc8e19

Android Studio在代码重构中的妙用的相关教程结束。

《Android Studio在代码重构中的妙用.doc》

下载本文的Word格式文档,以方便收藏与打印。