やりたいこと
リファクタリングp26に「一時変数の削除」という項目で、 以下のような記述があります。 これをなるだけ安全にIDEの機能を使って行う方法です。
totalAmount はループの中で代入されているので、 単純に「問い合せによる一時変数の置き換え(p.120)」を行うだけではなく、 問い合せメソッド内部にループをコピーする必要があります。
前提条件
IntelliJ IDEA 2018.1.6で確認しています。
概要
手順は長いですが、ポイントは2つ。
- できるだけIDEの機能を使う。
- 手作業で変更する場合は、「間違い=コンパイルエラー」になるように工夫する。
手順
元のコードは以下になります。 このtotalAmountを別メソッドにする方法です。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
まず、whileループを丸々コピーします。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
totalAmount += each.getCharge();
}
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
次に、whileループの処理を分けます。 前半からtotalAmountを消して、後半は逆にtotalAmountのみ残します。
ただし、Rental each
は両方とも必要なので残します。
もし消してしまってもコンパイルエラーになるので気付くはず。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
次に、totalAmountの定義を後半のwhileループの近くに移動します。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
double totalAmount = 0;
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
このままメソッドの抽出を行ってもいいのですが、 rentalsが2箇所で使用されているので、これを分割します。 今回は2度使用されている場所の分割ですが、 再代入している箇所のリファクタリングにも有効です。
まず、rentalsをrentals2(なんでもいいです)にリネームします。 IntelliJ IDEAでは「Refactor → Rename」で可能です。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals2 = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals2.hasMoreElements()) {
Rental each = (Rental) rentals2.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
double totalAmount = 0;
while (rentals2.hasMoreElements()) {
Rental each = (Rental) rentals2.nextElement();
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
その後、Enumeration rentals2 =
の行をエディタで
Enumeration rentals =
に変更します。
もちろんコンパイルエラーになるので、最初のwhileループのrentals2を エディタでrentalに変更します。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
double totalAmount = 0;
while (rentals2.hasMoreElements()) {
Rental each = (Rental) rentals2.nextElement();
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
2つ目のwhileのrentals2がコンパイルエラーになるため、 rentalsの定義をコピーして、rentals2として定義します。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
Enumeration rentals2 = _rentals.elements();
double totalAmount = 0;
while (rentals2.hasMoreElements()) {
Rental each = (Rental) rentals2.nextElement();
totalAmount += each.getCharge();
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
}
そして、メソッドの抽出を行います。
Enumeration rentals2
の行から、whileの終わりまでの6行を選択し、
IntelliJ IDEAでは、「Refactor→Extract→Method…」を選択します。
メソッド名をgetTotalChargeに変更し、OKを押します。
getTotalChargeのローカル変数名をリネームします。
- rentals2 → rental
- totalAmount → result
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
double totalAmount = getTotalCharge();
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
private double getTotalCharge() {
double result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getCharge();
}
return result;
}
}
最後に、totalAmountをインライン化することで、完了します。 IntelliJ IDEAでは、「Refactor→Inline…」です。
import java.util.Enumeration;
import java.util.Vector;
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
_name = name;
}
public void addRental(Rental arg) {
_rentals.addElement(arg);
}
public String getName() {
return _name;
}
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRentalPoints();
// この貸し出しに関する数値の表示
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
}
// フッタ部分の追加
result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) +
" frequent renter points";
return result;
}
private double getTotalCharge() {
double result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getCharge();
}
return result;
}
}
補足
マニュアル
- refactoring: まだありません。
- IntelliJ IDEA
- Java