컴퓨터가 인식 가능한 코드는 바보라도 작성할 수 있지만, 인간이 이해할 수 있는 코드는 실력 있는 프로그래머만 작성할 수 있다.
단순 비디오대여점에서 고객의 대여료 내역을 계산하고 출력하는 간단한 프로그램을 통해 리팩토링 기본을 배운다.
리팩토링 전 기본 코드
package com.pnpsecure.chap1;
import java.util.Enumeration;
import java.util.Vector;
public class Movie {
public static final int CHILDRENS = 2;
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
private String _title;
private int _priceCode;
public Movie(String title, int priceCode) {
_title = title;
_priceCode = priceCode;
}
public int get_priceCode() {
return _priceCode;
}
public void set_priceCode(int _priceCode) {
this._priceCode = _priceCode;
}
public String get_title() {
return _title;
}
}
class Rental {
private Movie _movie;
private int _daysRented;
public Rental(Movie movie, int daysRented) {
// TODO Auto-generated constructor stub
_movie = movie;
_daysRented = daysRented;
}
public int get_daysRented() {
return _daysRented;
}
public Movie get_movie() {
return _movie;
}
}
class Customer {
private String _name;
private Vector _rentals = new Vector();
public Customer(String name) {
// TODO Auto-generated constructor stub
_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 = getName() + "고객님 대여기록\n";
while (rentals.hasMoreElements()) {
double thisAmount = 0;
Rental each = (Rental) rentals.nextElement();
// 비디오 종류별 대여로 계산
switch (each.get_movie().get_priceCode()) {
case Movie.REGULAR:
thisAmount += 2;
if (each.get_daysRented() > 2) {
thisAmount += (each.get_daysRented() - 2) * 1.5;
}
break;
case Movie.NEW_RELEASE:
thisAmount += (each.get_daysRented()) * 3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if (each.get_daysRented() > 3) {
thisAmount += (each.get_daysRented() - 3) * 1.5;
}
break;
}
// 적립 포인트 계산
frequentRenterPoints++;
// 최신물 이틀 이산 대여하면 보너스 포인트 지급
if ((each.get_movie().get_priceCode() == Movie.NEW_RELEASE)
&& each.get_daysRented() > 1)
frequentRenterPoints++;
// 이번에 대여하는 비디오와 정보, 대여료 출력
result += "\t" + each.get_movie().get_title() + "\t"
+ String.valueOf(thisAmount) + "\n";
// 현재까지 누적된 총 대여료
totalAmount += thisAmount;
}
// 푸터행 추가
result += "누적된 대여료 : " + String.valueOf(totalAmount) + "\n";
result += "적립 포인트 : " + String.valueOf(frequentRenterPoints) + "\n";
return result;
}
}
위의 예제는 다음과 같은 문제가 있다.
너무 긴 Statement :
→ statement를 분해하여 각 부분을 알맞은 클래스로 옮기는 것, 코드중복을 줄이며 HTML로 내역을 출력하는 statement 메서드를 좀더 간편하게 작성할 수 있다.
→ 메서드 추출기법 사용 : swtich문은 하나의 메서드로 빼는 것이 적절한 덩어리 효력이 있는 매개변수와 지역변수를 확인한다. 현재 each변수와 thisAmount변수가 있으며 변경되는 변수는 thisAmount, 변경되지 않는 변수는 each변수이다. 변경되지 않는 변수는 매개변수로 전달할 수 있다.
대여료 계산을 메소드로 변경
private double amountFor(Rental each) {
int thisAmount = 0;
switch (each.get_movie().get_priceCode()) {
case Movie.REGULAR:
thisAmount += 2;
if (each.get_daysRented() > 2) {
thisAmount += (each.get_daysRented() - 2) * 1.5;
}
break;
case Movie.NEW_RELEASE:
thisAmount += (each.get_daysRented()) * 3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if (each.get_daysRented() > 3) {
thisAmount += (each.get_daysRented() - 3) * 1.5;
}
break;
}
return thisAmount;
}
변수명 변경
굳이 변수명을 바꿀 필요가 있나?
당연히 있다. 좋은 코드는 그것이 무슨 기능을 하는지 분명히 드러나야 하는데, 코드의 기능을 분명히 드러내는 열쇠가 바로 직관적인 변수명이다.
private double amountFor(Rental aRental) {
int result = 0;
switch (aRental.get_movie().get_priceCode()) {
case Movie.REGULAR:
result += 2;
if (aRental.get_daysRented() > 2) {
result += (aRental.get_daysRented() - 2) * 1.5;
}
break;
case Movie.NEW_RELEASE:
result += (aRental.get_daysRented()) * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (aRental.get_daysRented() > 3) {
result += (aRental.get_daysRented() - 3) * 1.5;
}
break;
}
return result;
}
대여료 계산 메서드 옮기기
amountFor 메서드는 Rental클래스의 정보를 이용하고 정작 자신이 속한 Customer클래스의 정보는 이용 하지 않는다. 따라서 이 메서드는 Rental클래스에 옮겨야 한다.
class Rental {
private Movie _movie;
private int _daysRented;
public Rental(Movie movie, int daysRented) {
// TODO Auto-generated constructor stub
_movie = movie;
_daysRented = daysRented;
}
public int get_daysRented() {
return _daysRented;
}
public Movie get_movie() {
return _movie;
}
public double getCharge() {
int result = 0;
switch (get_movie().get_priceCode()) {
case Movie.REGULAR:
result += 2;
if (get_daysRented() > 2) {
result += (get_daysRented() - 2) * 1.5;
}
break;
case Movie.NEW_RELEASE:
result += (get_daysRented()) * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (get_daysRented() > 3) {
result += (get_daysRented() - 3) * 1.5;
}
break;
}
return result;
}
}
class Customer {
…
private double amountFor(Rental aRental) {
return aRental.getCharge();
}
}
변수 중복 제거
thisAmount는 each.charge 메서드의 결과를 저장하는데만 사용되고 그 후엔 전혀 사용되지 않는다. 따라서 메서드 호출전환 기법을 사용하여 thisAmount변수를 삭제한다.
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = getName() + "고객님 대여기록\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
// 적립 포인트 계산
frequentRenterPoints++;
// 최신물 이틀 이산 대여하면 보너스 포인트 지급
if ((each.get_movie().get_priceCode() == Movie.NEW_RELEASE)
&& each.get_daysRented() > 1)
frequentRenterPoints++;
// 이번에 대여하는 비디오와 정보, 대여료 출력
result += "\t" + each.get_movie().get_title() + "\t"
+ (each.getCharge()) + "\n";
// 현재까지 누적된 총 대여료
totalAmount += each.getCharge();
}
// 푸터행 추가
result += "누적된 대여료 : " + String.valueOf(totalAmount) + "\n";
result += "적립 포인트 : " + String.valueOf(frequentRenterPoints) + "\n";
return result;
}
적립 포인트 계산을 메서드로 빼기
대여료 부분만큼 여러 조건으로 나뉘진 않지만, 대여비디오의 종류에 따라 적립 포인트 계산법이 달라진다. 이 부분의 처리코드도 Rental클래스에 넣는 것이 합리적이다.
class Rental {
…
public int getFrequentRenterPoints(){
//최신물 2틀이상 대여 : 2포인트
//그외 1포인트 지급
if(get_movie().get_priceCode() == Movie.NEW_RELEASE && get_daysRented() > 1)
return 2;
else
return 1;
}
}
class Customer {
…
public String statement() {
…
// 적립 포인트 계산
frequentRenterPoints += each.getFrequentRenterPoints();
'IT > Programming' 카테고리의 다른 글
[JAVA] String toLowerCase() (0) | 2023.04.19 |
---|---|
<리팩토링> 소스코드 리팩토링 기본 개념, 두번째 (0) | 2023.04.19 |
<Code Craft> 1부 코드와 마주보기 : 01 방어하기 (2) (0) | 2023.04.19 |
<Code Craft> 1부 코드와 마주보기 : 01 방어하기 (1) (0) | 2023.04.19 |
JSP 자바빈즈(JavaBeans) (0) | 2023.04.19 |