第四章
Refactoring Code
The solutions you develop in Android Studio will not always follow a straight path from design to finish. To be an effective Android programmer, you need to be flexible and refactor your code as you develop, debug, and test. In the preceding chapter, you learned how Android Studio can generate code; in this chapter, you’re going to see how Android Studio can refactor your code. The greatest risk with refactoring code is that you may introduce unintended errors. Android Studio mitigates these risks by analyzing the consequences of certain risky refactoring operations, and then activates the Find tool window, in which you may preview your changes—flagged with any errors or conflicts—before committing them.
<翻译>
第四章
重构代码
在Android Studio中开发,解决方案不会总是一蹴而成的。作为一个有效率的编程者,在你的开发,调试和测试中需要一些弹性以及代码重构。随着在这章中的行进,你将明白Android Studio如何产生代码;在这章里你将看到Android Studio如何重构你的代码。重构代码最大的风险是可能引入不期望的错误。通过分析某些风险重构操作的结果,Android Studio减低了这些风险,然后激活Find tool窗口,在发行前,你可以预览你的更改-那里标志着任何错误或冲突。
Many of the refactoring operations presented in this chapter can also be performed without Android Studio’s refactoring tools. However, you should avoid refactoring by brute force (for example, by resorting to a global find-and-replace option) because Android Studio cannot always save you from introducing errors in those circumstances. In contrast, if Android Studio detects that you’re attempting a refactoring operation, it will try to prevent
you from making any stupid mistakes. For example, dragging a Java source file from one package to another in the Project tool window will force a Refactor ➤ Move operation, which analyzes the consequences of your move operation, allows you to preview the changes, and then gracefully changes any import statements for that class throughout your entire project to the new fully qualified package name.
<翻译>
在这章里描述的许多重构操作也可不用Android Studio的重构工具来执行。无论怎样,你必须避免强行重构(例如:通过一个全文的查找/替换操作来重组代码),因为在这些情形下Android Studio不能从引入的错误中一直保护你。相反,如果Android Studio发现你企图一个重构操作,它将尽力避免你犯任何愚蠢错误。例如,在Project tool窗口中拖动一个JAVA源文件从一个包到另一个包,这将强制产Refactor ➤ Move操作,由此会分析你的移动操作结果,允许你预览那些改变,然后适当地改变类中所有的import语句以适应合格的新全包名。
Most refactoring operations are confined to one method or one class, and thus will not likely introduce errors into your project. Risky refactoring operations are those that involve two or more assets. If a refactoring operation introduces compilation errors, the Inspections Manager will flag the affected assets with red tags in the Editor. At that point, you can either attempt to fix them, or simply undo the entire refactoring operation by pressing Ctrl+Z | Cmd+Z. If the refactor operation succeeded with no compilation errors, but nevertheless involved a lot of assets, you should still run your tests to verify that you have not introduced any runtime errors. Chapter 11 covers testing.
<翻译>
绝大多数的重构操作被限制于一个方法或一个类里面,因而将不太会引入错误到你的项目中。危险的重构操作是那些涉及到两个或以上的资源文件。如果重构操作引入编译错误,检查管理器将在编辑器里用红标签标志那些被影响的资源文件。基于这点,你或者可以企图去修正它们,或者用Ctrl+Z|Cmd+Z简单地取消这个重构操作了。如果重构操作以没有编译错误完成,然而涉及到太多资源文件,那你还是必须要测试及校验你是否引入了任何实时错误。第11章涵盖了测试。
Tip: You should commit any significant refactoring changes as a single Git commit so that you can easily revert that commit later. Chapter 7 covers Git.
<翻译>
提示:你必须提交任何重要的重构更改,作为单一的Git提交,因此之后你可以轻松地回复那个提交。第7章涵盖了Git。
This chapter focuses on the refactoring operations with the greatest utility. Before we begin addressing individual refactoring operations, we’d like to point out that Android Studio has an extremely convenient refactoring operation called Refactor ➤ Refactor This. Choosing this option displays a context menu, shown in Figure 4-1, that aggregates the most useful refactoring operations. The keyboard shortcut for this operation is Ctrl+Alt+Shift+T | Ctrl+T, and on a PC you can remember it by its conveniently mnemonic acronym: CAST.
<翻译>
这章聚焦于用最好的工具来执行重构操作。在我们探访独立的重构操作之前,我们乐于指出Android Studio有一个极其便利的重构工具叫Refactor ➤Refactor This.总在上下文菜单中选择这个,图4-1所示,集成了绝大多数有用的重构操作。快捷键是Ctrl+Alt+Shift+T | Ctrl+T,在PC上你可以方便地记首字母CAST。
图4-1:重构器Refactor This菜单,包括了大部分的有用重构操作。
Before you begin working on the examples in this chapter, modify the Sandbox.java file from Chapter 3 so that it extends nothing and contains neither methods nor members, like the following snippet:
public class Sandbox {
}
<翻译>
在你开始本章的例子之前,修改第3章的Sandbox.java文件,它没扩充和包含任何方法或成员变量,象下面的小片段:
public class Sandbox {
}
Rename
Select Sandbox from the Project tool window and then navigate to Refactor ➤ Rename or press Shift+F6. The resulting dialog box allows you to rename your class and rename any additional occurrences of that name in comments, test cases, and inherited classes. Rename Sandbox to Playpen and click the Refactor button, shown in Figure 4-2. You should see the results of the Renaming operation in your project. Now undo the Renaming
operation by pressing Ctrl+Z | Cmd+Z.
<翻译>
重命名
在Project tool窗口中选择Sandbox并导航到Refactor➤ Rename或按Shift+F6.引出一个对话框以允许你重命名你的类,另外也可以重命名出现在注释中,测试实例,以及继承类中的那些名字。重命名Sandbox为Playpen并点击Refactor按钮,如图4-2所示。你将看到在你项目中重命名的操作。现在请用Ctrl+Z | Cmd+Z取消这个操作。
图4-2:重命名Sandox为Playpen
Change Signature
The Change Signature operation enables you to change the following properties of a method:visibility, name, return type, parameters, and exceptions thrown. Create a method inSandbox.java, as shown in this code snippet:
public String greetings(String message){
return "Hello " + message;
}
<翻译>
修改签名
更改签名的操作允许你改变方法的下列属性:可视性,名称,返回类型,参数,以及例外抛出。在Sandbox.java里创建一个方法,如下面的代码片断:
public String greetings(String message){
return "Hello " + message;
}
Place your cursor anywhere on the word greetings (highlighted in bold) and press Ctrl+F6 | Cmd+F6, or navigate to Refactor ➤ Change Signature. The resulting dialog box enables you to modify the signature of the method, as shown in Figure 4-3.
<翻译>
将光标移到单词greetings上面(高亮的粗体字)并按Ctrl+F6 |Cmd+F6,或导航到Refactor ➤ Change Signature。结果对话框会允许你修改方法的签名,如图4-3所示。
图4-3:修改签名对话框
In the Parameters tab, click the String message item. Change the name of the String parameter from message to greet, as shown in Figure 4-3. The green-plus and red-minus icons allow you to add parameters to or subtract parameters from your method, respectively; and you may edit their types and names in the list. In addition to modifying the current method, you may decide to select the Delegate via Overloading Method radio button. Selecting this radio button will leave your original method unaffected, but generate another method with the new signature you define. A set of methods may be considered overloaded in Java if they have
the same name, but the parameter order and/or parameter types are different. However, the change we made does not qualify this method for overloading. You may preview your changes before committing them by clicking the Preview button if you so choose. To complete the operation and dismiss the dialog box, click the Refactor button.
<翻译>
在参数表里,点击字符串类型参数message这项,将它的名称从message改为greet,如图4-3所示。图标绿色的+和红色的-允许你分别增加或减少方法的参数。并且,你也可以编辑参数表里面它们的类型和名称。另外,修改当前方法时,你可能决定选择委托重载方法的单选按钮。选择这个按钮将保留你的原始方法不受影响,但会生成另一个你新定义签名的方法。在JAVA里如果你可能考虑一系列有相同的名称重载方法,但参数的顺序和/或参数的类型不同。无论如何,你做的更改并没限制方法的重载。如果你希望的话通过点击预览按钮,可以在提交它们之前预览。按Refactor按钮完成并离开。
Type Migration
As the name suggests, type migration allows you to migrate from one Java type to another. Let’s assume that you create a Person class. Further along in your development, you discover that Person is too generic, so you create a Manager class that extends Person. If you want to migrate all instances of Person to Manager, you can do this easily with type migration.
<翻译>
类型移植
如标题所述,类型移植允许你从一个JAVA类型移植到另一个。让我们假设你创建了一个Person类。随着你的开发进行,你发现Person太一般了,因而你又创建了一个Manager类扩展于Person。如果你想移植所有的Person实例到Manager,你可以用类型移植轻松办到。
Place your cursor on the String declaration (highlighted in bold in the following code snippet) of the greetings method and press Ctrl+Shift+F6 | Cmd+Shift+F6 or choose Refactor ➤ Type Migration. The resulting dialog box resembles that seen in Figure 4-4.
public String greetings(String greet){
return "Hello " + greet;
}
<翻译>
将光标放到greetings方法的String声明上(在下面代码片段中的高亮粗体字)并且按Ctrl+Shift+F6 | Cmd+Shift+F6或选择Refactor ➤ Type Migration。结果对话框如图4-4所示。
public String greetings(String greet){
return "Hello " + greet;
}
图4-4:类型移植,从String到date
Change java.lang.String to java.util.Date, as shown in Figure 4-4. Select Open Files from the Choose Scope drop-down list. As with most refactor operations, you can preview your changes by clicking the Preview button. Click the Refactor button.
<翻译>
更改java.lang.String到java.util.Date,如图4-4所示。从选择范围框的下拉菜单里选择打开文件。如同大多数重构操作,通过点击预览按钮,你可预览你的更改。这里我们按Refactor按钮。
Move
You can move a source file in one of three ways:
By dragging the source file from one package to another in the Project tool window
By selecting that source file and navigating to Refactor ➤ Move from the main menu
By selecting the file in the Project tool window and pressing F6
Right-click (Ctrl-click on Mac) the com.apress.gerber.helloworld package and choose New ➤ Package. Name the package refactor. From the Project tool window, drag and drop the Sandbox class to the refactor package and press OK when prompted by the dialog shown in Figure 4-5. Any drag-and-drop operation you perform in the Project tool window automatically forces a Refactor ➤ Move operation, which allows you to safely move your class from one package to another.
<翻译>
移动
你可以通以下三种方法中的一个方法来移动源代码文件。
在Project tool窗口拖动源文件从一个包到另一个包中;
选择源文件从主菜单导航到Refactor ➤ Move;
在Project tool窗口选择源文件并按下F6。
右键点击(在Mac电脑上Ctrl-click)com.apress.gerber.helloworld包并选择New ➤ Package。给这个重构目标包命名。从Project tool窗口,把sandbox.java拖放到重构目标包里,当出现图4-5对话框时按OK。在Project tool窗口中执行的任何拖放操作将自动产生一个重构移动操作,这个会让你安全地把一个类从一个包移动到别一个包里。
图4-5:由于拖放操作导致的重构移动对话框
In addition to moving classes, you may also move members. In your Sandbox class, define a new member like the following:
public static final String HELLO = "Hello Android Studio";
<翻译>
除了移动类之外,你也可以移动成员变量。在你的Sandbox.class中,定义一个新成员变量如下:
public static final String HELLO = "Hello Android Studio";
Place your cursor on this line of code and press F6. The resulting dialog box allows you to move members from one class to another, as shown in Figure 4-6. Click the Cancel button to cancel this operation.
<翻译>
移动光标到这行代码上并按下F6。导致一个对话框允许你移动成员变量从一个类到另一个。如图4-6所示。请点击取消按钮来取消这次操作。
图4-6:移动成员变量的对话框
Copy
Copy, which is similar to Move, is accessed by pressing the keyboard shortcut F5 or by
choosing Refactor ➤ Copy from the main menu. In the Project tool window, select the Sandbox class in the refactor package and press F5. Select the com.apress.gerber.helloworld package from the Destination Package drop-down menu and click OK, as shown in Figure 4-7. Copying Java source files indiscriminately as we did here is not a good idea because the resolution is ambiguous and thus rife for potential errors.
<翻译>
复制
复制,有点象移动,按快捷键F5或选择主菜单里的Refactor ➤ Copy来访问。在Project tool窗口,选择之前重构的包中的Sandbox.java并按下F5键。在目标包的拖放菜单中选择com.apress.gerber.helloworld包并点击OK,如图4-7所示。象我们这里无差别地复制JAVA源文件不是一个好主意,因为结果不明确且普通存在着错误。
图4-7:复制类的对话框
Safe Delete
Let’s delete the copied class we created. You can always delete files and resources in Android Studio by selecting them in the Project tool window and pressing the Delete key. Click the Sandbox file in the refactor package and press Delete. The resulting dialog box allows you to use the Safe Delete option by selecting the Safe Delete check box. The advantage of using Safe Delete is that we can search any dependencies on the asset that might be broken before performing the delete, as shown in Figure 4-8. If any dependencies of this asset are found in your project, you will be given the option to view them, or force the delete operation anyway by clicking Delete Anyway.
<翻译>
安全删除
让我们来删除刚才复制的类。在Android Studio的Project tool窗口里用Delete按键,你总可以删除文件和资源。在刚才重构包中点击Sandbox.java文件并按下Delete键。结果对话框允许你使用安全删检查选项来删除。安全删除的先进之处在于执行删除前我们可查询任何因依赖于这个资产而可能导致的破坏,如图4-8所示。如果在这个项目中发任何东西依赖于这个资产,将给出一个可选项浏览它们,或点击无论如何都删除可选项来强制删除。
图4-8:安全删除对话框
Extract
Extract isn’t just one operation but several. This section covers some of the more important extract operations: Extract Variable, Extract Constant, Extract Field, Extract Parameter, and Extract Method. In the Sandbox class, let’s start with a clean slate by removing all the members and methods:
public class Sandbox {
}
<翻译>
析出
析出不是一个操作而是几个。这节涵括了一些重要的析出操作:析出变量,析出常量,析出域,析出参数,和析出方法。在Sandbox.class里,让我们先移走所有的成员变量和方法从白纸开始:
public class Sandbox {
}
Extract Variable
In your Sandbox.java class, define a method, as shown here:
private String saySomething(){
return "Something";
}
<翻译>
析出变量
在你的Sandbox.java类里,定义一个方法如下示:
private String saySomething(){
return "Something";
}
Place your cursor anywhere on the hard-coded Something value (highlighted in bold) and
choose Refactor ➤ Extract ➤ Variable, or press Ctrl+Alt+V | Cmd+Alt+V and then press Enter without selecting the Declare final checkbox. Android Studio extracts a local variable and names it according to the hard-coded String. You should end up with something like this:
private String saySomething(){
String something = "Something";
return something;
}
<翻译>
把光标放到硬编码Something的值(粗体字)上并选择Refactor ➤ Extract ➤ Variable,或者按Ctrl+Alt+V | Cmd+Alt+V接着按回车,不要选择声明final的复选框。Android Studio依据硬编码字符串析出一个本地变量并命名它。你将以如下代码告终:
private String saySomething(){
String something = "Something";
return something;
}
Extract Constant
As you develop apps in Android, you will find yourself using a lot of Strings as keys—for
example, in Maps and Bundles. Therefore, extracting constants will save you a lot of time.
<翻译>
析出常量
当你开发Android的APP时,会发现你会用很多的字符串作为键-例如,在Map和Bundle里。因此,析出常量将会节省你大量的时间。
Define a method like the one seen in the following code snippet. Place your cursor anywhere on the name_key string and press Ctrl+Alt+C | Cmd+Alt+C. The resulting dialog box should look like Figure 4-9. Here, Android Studio provides a few suggestions for names. By convention, constants in Java are all caps. Select NAME_KEY and press Enter.
Note You will need to import android.os.Bundle in order to create the proceding method without compile-time errors.
private void addName(String name, Bundle bundle ){
bundle.putString("name_key", name);
}
<翻译>
定义一个方法象如下的代码片段。把光标放到name_key字符串上并按Ctrl+Alt+C | Cmd+Alt+C。结果对话框将如图4-9所示。这里,Android Studio提供一些建议的名称。按惯例,常量在JAVA里都应该是大写的。选择NAME_KEY并按回车。
注意:为了创建和处理这个方法而不发生编译错误,你必须导入android.os. Bundle
private void addName(String name, Bundle bundle ){
bundle.putString("name_key", name);
}
You should end up with a constant called NAME_KEY, which should be defined like this:
public static final String NAME_KEY = "name_key";
<翻译>
结束时你将看一个常量名称是NAME_KEY,象以下定义的:
public static final String NAME_KEY = "name_key";
图4-9:析出常量NAME_KEY
Extract Field
Extract Field converts a local variable to a field (a.k.a. member) of your class.
Note You will need to import java.util.Date in order to create the proceding method without compile-time errors.
Define a method in your Sandbox class:
private Date getDate(){
return new Date();
}
<翻译>
析出域
析出域将转换一个本地变量到你的类域(a.k.a.成员变量)里。
注意:为了创建和处理这个方法而不发生编译错误,你必须导入android.os. Bundle
在你的Sandbox类里定义一个方法:
private Date getDate(){
return new Date();
}
Place your cursor anywhere on Date (highlighted in bold) and press Ctrl+Alt+F | Cmd+Alt+F. You will see a dialog box like the one shown in Figure 4-10. In Android, the naming convention is to prefix fields (a.k.a. members) with an m. You will also notice a drop-down menu that allows you to initialize your field in the current method, the field declaration, or the constructor. Select Field Declaration and press Enter.
<翻译>
把光标放到Date(粗体高亮)上并按键Ctrl+Alt+F | Cmd+Alt+F。你将看到一个对话框如图4-10所示。在Android里,按惯例域(a.k.a. 成员变量)名的头字母用m。你将会注意到一个下拉菜单允许你初始化当前方法中的域,域声明,或构造器。选择域声明并按回车。
图4-10:析出域对话框
You should end up with something like this:
private final Date mDate = new Date();
...
private Date getDate(){
return mDate;
}
Remove the final keyword so the declaration line looks like the following code snippet:
private Date mDate = new Date();
<翻译>
结果如下:
private final Date mDate = new Date();
...
private Date getDate(){
return mDate;
}
移除final关键字,结果这个域声明如下:
private Date mDate = new Date();
Extract Parameter
Extract Parameter allows you to extract a variable and place it as a parameter of the enclosing method. Define a method in your Sandbox class:
private void setDate(){
mDate = new Date();
}
<翻译>
析出参数
析出参数允许你析出一个变量并放到封套的方法里。在你的Sandbox类里定义一个方法:
private void setDate(){
mDate = new Date();
}
Place your cursor anywhere on Date() (highlighted in bold), press Ctrl+Alt+P | Cmd+Alt+P, and press Enter. The resulting method should look like the following code snippet:
private void setDate(Date date){
mDate = date;
}
<翻译>
将光标放到Date()(粗体高亮)上,按Ctrl+Alt+P | Cmd+Alt+P接着按回车。结果这个方法如下代码片段所示:
private void setDate(Date date){
mDate = date;
}
Extract Method
Extract Method lets you select one or more lines of contiguous code and place them in a separate method. There are two reasons you would want to do this. The first reason is that you have a method that is too complex. Breaking an algorithm into discrete blocks of approximately 10–20 lines each is much easier to read and far less error-prone than one method with 100 lines of code.
<翻译>
析出方法
析出方法让你选择一行或多行行连续的代码放到一个分离的方法中。有两个原因你会想做这个。首先是方法太复杂了。相对于100行的代码,打断它到约10到20行的算法块会比较容易阅读且会较少错误。
It’s almost never a good idea to repeat a block of code, so if you find a block of code that is repeated, it’s best to extract a method and call that method in place of the repeated blocks. By extracting a method and calling it where you had previous used a repeated block of code, you can maintain your method in one place, and if you need to modify it, you need only modify it once. Re-create the following two methods in your Sandbox class, as shown in Listing 4-1. Feel free to copy and paste.
private String methodHello (){
String greet = "Hello";
StringBuilder stringBuilder = new StringBuilder();
for(int nC = 0; nC < 10; nC++){
stringBuilder.append(greet + nC);
}
return stringBuilder.toString();
}
private String methodGoodbye (){
String greet = "Goodbye";
StringBuilder stringBuilder = new StringBuilder();
for(int nC = 0; nC < 10; nC++){
stringBuilder.append(greet + nC);
}
return stringBuilder.toString();
}
<翻译>
再者,重复的代码从来就不是好主意,因而如果你发现了重复的代码,最好析出它们成一个方法并在重复的地方调用它。通过析出之前重复的代码到一个方法里并调用它,你可以保留你的方法在一个地方,如果你想修改它,你只要修改一次。在你的Sandbox类里重建下面的两个方法如清单4-1。你可以*地复制和粘贴。
清单4-1
private String methodHello (){
String greet = "Hello";
StringBuilder stringBuilder = new StringBuilder();
for(int nC = 0; nC < 10; nC++){
stringBuilder.append(greet + nC);
}
return stringBuilder.toString();
}
private String methodGoodbye (){
String greet = "Goodbye";
StringBuilder stringBuilder = new StringBuilder();
for(int nC = 0; nC < 10; nC++){
stringBuilder.append(greet + nC);
}
return stringBuilder.toString();
}
As we’ve already mentioned, any time you find yourself repeating blocks of code or copying and pasting a block of code, you should consider using Extract Method. Select all the lines highlighted in bold in Listing 4-1. Now press Ctrl+Alt+M | Cmd+Alt+M to extract the method. You will be presented with a dialog box showing the signature of the method. Rename this method to getGreet, as shown in Figure 4-11, and click OK.
<翻译>
象我们已经提到过的,任何时候你发现重复一个代码块或复制和粘贴代码块,你就必须考虑析出方法了。选择清单4-1中粗体高亮的块。现在按Ctrl+Alt+M | Cmd+Alt+M析出方法。将呈现一个对话框列出方法的签名。重命名这个方法为getGreet,如图4-11所示,接着点击OK。
图4-11:析出方法对话框
Android Studio scans your file and sees that you have another instance of the exact block of code. Click Yes to accept the suggestions in the Process Duplicates dialog box, as shown in Figure 4-12.
<翻译>
Android Studio扫描你的文件并看到你有另外一个确凿的代码块案例。在处理复制对话框里点击Yes接受建议,如图4-12所示。
图4-12:复制处理对话框
You should end up with something like Listing 4-2. The resulting method is far easier to maintain now that it is kept in one place.
Listing 4-2. Code Resulting from Extract Method Operation
private String methodHello (){
String greet = "Hello";
return getGreet(greet);
}
private String methodGoodbye (){
String greet = "Goodbye";
return getGreet(greet);
}
private String getGreet (String greet){
StringBuilder stringBuilder = new StringBuilder();
for(int nC = 0; nC < 10; nC++){
stringBuilder.append(greet + nC);
}
return stringBuilder.toString();
}
<翻译>
你将看到清单4-2的代码。现在结果方法很容易保留在某个地方。
清单 4-2.从析出方法操作里得出的结果代码
private String methodHello (){
String greet = "Hello";
return getGreet(greet);
}
private String methodGoodbye (){
String greet = "Goodbye";
return getGreet(greet);
}
private String getGreet (String greet){
StringBuilder stringBuilder = new StringBuilder();
for(int nC = 0; nC < 10; nC++){
stringBuilder.append(greet + nC);
}
return stringBuilder.toString();
}
Advanced Refactoring
The refactoring operations presented throughout the remainder of this chapter are advanced. If you’re interested in simply getting up to speed with Android Studio, you already have sufficient knowledge to use the refactoring operations effectively, and you may skip this section. However, if you understand Java well and want to take a deep dive into some of the more advanced refactoring operations, continue reading.
<翻译>
高级重构
整个这章所描述的剩下的其他重构操作是高级别的。如果你有兴趣简单迅速地提高Android Studio,已经有了足够的知识来有效地使用重构操作,也许你可以跳过这节。但无论如何,如果你对JAVA有比较好的理解并且想钻研更多的重构操作,继续看下去吧。
Start with a clean slate by removing all method and members from Sandbox.java:
public class Sandbox {
}
Right-click (Ctrl-click on Mac) the com.apress.gerber.helloworld package in the Project tool window and choose New ➤ Java Class. Name your class Minibox. Change the definition of Minibox so that it inherits from Sandbox and has a member called mShovel, as shown here:
public class Minibox extends Sandbox {
private String mShovel;
}
<翻译>
移除Sandbox.java里的所有成员变量及方法,从一张白纸开始。
public class Sandbox {
}
右键点击(Mac电脑用Ctrl+点击)Project tool窗口里的com.apress.gerber.helloworld包,选择New ➤ Java Class。命名这个类为Minibox。修改Minibox类定义继承自Sandbox并且有个成员变量叫做mShovel,如这里所示:
public class Minibox extends Sandbox {
private String mShovel;
}
Push Members Down and Pull Members Up
Pushing members down and pulling members up is used with inheritance. Notice that we have defined the mShovel member in the Minibox class. Let’s assume we decide later that mShovel may be useful for other classes that extend Sandbox. To do this, open the Minibox class and choose Refactor ➤ Pull Members Up. The resulting dialog box looks like Figure 4-13.
<翻译>
推高成员变量以及拉低成员变量
推高成员变量以及拉低成员变量用于继承。注意到我们定义了一个mShovel成员变量在Minibox类里。让我们假设mShovel可能对于其他继承自Sandbox类的子类也有用。这样做,打开Minibox类并选择Refactor ➤ Pull Members Up。结果对话框如图4-13所示。
图 4-13: 推高成员变量对话框
The mShovel member is selected by default and the Pull Up Members combo box is set to the com.apress.gerber.helloworld.Sandbox class by default since Sandbox is the superclass of Minibox. Click Refactor. If you now inspect Sandbox and Minibox, you will notice that the mShovel member belongs to Sandbox and is no longer present in Minibox. As a general rule, if you believe that a member may be useful to other extending classes, you should pull those members up the hierarchy. To push members down the hierarchy, you can follow similar steps.
<翻译>
mShovel会被缺省地选中,并且因为Sandbox是Minibox的父类所以推高成员变量组合窗口会缺省地设定在com.apress.gerber.helloworld.Sandbox类上。点击Refactor,检视Sandbox和Minibox,你将发现mShovel成员变量不再出现在Minibox里了,而属于Sandbox。作为一般规则,如果你觉得一个成员变量对其他扩展类也是有用的,那么就把这些成员变量在继承层次里推高。用类似的手法可以拉低成员变量。
Replace Inheritance with Delegation
Right-click (Ctrl-click on Mac) the com.apress.gerber.helloword package and choose New ➤ Java Class. Name your class Patio and make it extend Sandbox:
public class Patio extends Sandbox {
}
<翻译>
解除继承并委托成员变量
右键点击(Mac电脑用Ctrl+点击)com.apress.gerber.helloword包选择New ➤ Java Class。命名这个新类为Patio并继承自Sandbox:
public class Patio extends Sandbox {
}
Upon further analysis, we decide that Patio is not a Sandbox, but rather has a Sandbox. To change this relationship, navigate to Refactor ➤ Replace Inheritance with Delegation. In the resulting dialog box, click the Generate Getter for Delegated Component check box, shown in Figure 4-14.
<翻译>
进一步分析后,我们决定Patio不是一个Sandbox,但更象是它拥有一个Sandbox。为了改变它们的关系,导航到Refactor ➤ Replace Inheritance with Delegation。在结果对话框里,点击为委托成产生getter方法,如图4-14所示。
图 4-14:解除继承并委托成员变量对话框
Your Patio class should now have a Sandbox member, as shown in the following code snippet:
public class Patio {
private final Sandbox mSandbox = new Sandbox();
public Sandbox getSandbox() {
return mSandbox;
}
}
<翻译>
现在你的Patio类将拥有一个Sandbox成员变量,如下代码片段所示:
public class Patio {
private final Sandbox mSandbox = new Sandbox();
public Sandbox getSandbox() {
return mSandbox;
}
}
Encapsulate Fields
Encapsulation is an object-oriented strategy that hides class members by making their access level private and then provides a public interface to those members via public getter/setter methods. Refactor ➤ Encapsulate Fields is similar to Code ➤ Generate ➤ Getter and Setter, though you have a lot more options when you choose Refactor ➤ Encapsulate Fields. Open your Sandbox class and define a new member called mChildren, as highlighted in bold
in the next code snippet. From the main menu, choose Refactor ➤ Encapsulate Fields.
public class Sandbox {
private String mShovel;
private int mChildren;
}
封装域
<翻译>
封装域是一种面向对象策略,通过使访问级别为私有来隐藏类成员变量并提供以公开的getter/setter方法作为公开的接口。尽管当你选择Refactor ➤ Encapsulate Fields时有很多的选择,但Refactor ➤ Encapsulate Fields类似于Code ➤ Generate ➤ Getter and Setter。打开你的Sandbox类并定义一个成员变量叫mChildren,如下面代码片段中的粗体高亮部分。从主菜单,选择Refactor ➤ Encapsulate Fields。
public class Sandbox {
private String mShovel;
private int mChildren;
}
The resulting dialog box allows you to choose exactly how your fields will be encapsulated and what access level they should have. A truly encapsulated field will have private visibility with public accessor (getter) and mutator (setter) methods. Click the Refactor button, shown in Figure 4-15, and notice that Android Studio has generated getters and setters for us in our Sandbox.java class.
<翻译>
结果对话框允许你精确地选择怎样封装这些域以及它们的访问级别。真正地封装域将有一个私有的可视度及公有的访问(getter)及改变(setter)方法。点击Refactor按钮,如图4-15所示,注意到Android Studio在我们的Sandbox.java类里为我们生成所有的getter和setter。
图 4-15:封装域对话框
Wrap Method Return Value
Wrapping a return value may be useful when you need to return an object rather than a primitive (though there are other scenarios where you might want to wrap a return value). Place your cursor on the getChildren() method and navigate to Refactor ➤ Wrap Method Return Value. Select the Use Existing Class check box and type java.lang.Integer as the Name and value as the Wrapper Field, as shown in Figure 4-16. Now click Refactor and notice that your getChildren() method returns an Integer object rather than a primitive int.
<翻译>
包装方法的返回值类型
当你需要返回一个对象而不是基本数据类型时,包装返回值可能是有用的(当然还有其他的情形你可能想包装一个返回值)。把光标放到getChildren()方法上并导航到Refactor ➤ Wrap Method Return Value。选择Use Existing Class选项,名称框输入java.lang.Integer,包装域框则选择value,如图4-16所示。现在点击Refactor按钮且注意到你的getChildren()方法返回一个Integer类,而不是基本数据类型int。
图 4-16: 包装返回值对话框
Replace Constructor with Factory Method
Place your cursor inside the enclosing brackets of the Sandbox class definition. Press Alt+Insert | Cmd+N and select Constructor to generate a new constructor. Choose both members, shown in Figure 4-17, and click OK.
<翻译>
用工厂方法代替构造器
把光标放在Sandbox类定义附栏内。按Alt+Insert | Cmd+N接着选择生成一个新构造器。选择所有成员变量,如图4-17所示,然后点击OK。
图 4-17:通过构造器来选择域的对话框
Place your cursor anywhere in the newly defined constructor, shown in the following code snippet, and then navigate to Refactor ➤ Replace Constructor with Factory Method. The resulting dialog box looks like Figure 4-18. Click Refactor to generate a factory method.
public Sandbox(String shovel, int children) {
mShovel = shovel;
mChildren = children;
}
<翻译>
将光标放到如下所示代码片段构造器的任何位置,并导航到Refactor ➤ Replace Constructor with Factory Method。结果对话框象图4-18所示。点击Refactor来生成一个工厂方法。
public Sandbox(String shovel, int children) {
mShovel = shovel;
mChildren = children;
}
图 4-18: 用工厂模式代替构造器的对话框
Notice that the constructor is now private and that a new static method returns an instance of the Sandbox class, as shown in the following code snippet. This operation is particularly useful if you are creating a singleton.
public static Sandbox createSandbox(String shovel, int children) {
return new Sandbox(shovel, children);
}
<翻译>
注意到之前的构造器现在变成private了并且一个新的静态方法返回Sandbox类的实例,如下面代码片段所示。如果你正创建一个单例类,这将会是非常有用的。
public static Sandbox createSandbox(String shovel, int children) {
return new Sandbox(shovel, children);
}
Convert Anonymous to Inner
In the constructor of your Sandbox class, add the following line:
new Thread(new Runnable()).start();
<翻译>
转换匿名内部类
在你的Sandbox类的构造器里加上下面行:
new Thread(new Runnable()).start();
Place your cursor on Runnable() and press Alt+Enter to invoke the code-completion operation. Then select Implement Methods. Select the run method and click OK. Your code should look something like the following code snippet:
new Thread(new Runnable() {
@Override
public void run() {
//do something
}
}).start();
<翻译>
把光标放在Runnable()上并按下Alt+Enter来调用完成代码操作。然后选择实现方法。选择run()方法并点击OK。你的代码将象下面片段所示:
new Thread(new Runnable() {
@Override
public void run() {
//do something
}
}).start();
Place your cursor on Runnable() and navigate to Refactor ➤ Convert Anonymous to Inner. Android Studio suggests MyRunnable as a class name for you, as shown in Figure 4-19. Deselect the Make Class Static check box and click OK. Notice that you now have a private inner class called MyRunnable in Sandbox.java that implements the Runnable interface. This example doesn’t do much; however, you may have opportunities to use this operation when delegating the behaviors of Views.
把光标放在Runnable()上并导航到Refactor ➤ Convert Anonymous to Inner。Android Studio建议MyRunnable作为这个类名,如图4-19所示。去选Make class static选项后点击OK。注意到你将在Sandbox类里得到一个私有的内部类叫MyRunnable,它实现Runnable接口。这个例子没有做更多的事;不管怎样,当委派View的行为时你也许有机会用到这个。
图 4-19: 转换匿名内部类的对话框
Summary
This chapter discussed many of the refactoring operations available in Android Studio. Refactoring code is a necessary part of any programming project, and the refactoring tools in Android Studio are among the best. Android Studio mitigates the risk of performing certain refactoring operations by analyzing the consequences and allowing you to preview the results in the Find tool window prior to committing an operation. The most important refactoring operations are available from the Refactor ➤ Refactor This dialog box, which is invoked by using the keyboard shortcut Ctrl+Alt+Shift+T | Ctrl+T.
<翻译>
小结
这章讨论了Android Studio目前有效的许多重构操作。重构代码是编程工程的一个必需的部分,Android Studio里的重构工具是最好的重构工具之一。Android Studio通过分析推理以及允许你于提交操作前在Find tool窗口预览结果,减低了某些操作执行的风险。最重要的是重构操作Refactor ➤ Refactor This对话框是如此有效的,这个可以通过用快捷键Ctrl+Alt+Shift+T | Ctrl+T来调用。