IT/Programming / / 2023. 4. 19. 10:21

<리팩토링> 소스코드 리팩토링 기본, 첫번째

반응형
컴퓨터가 인식 가능한 코드는 바보라도 작성할 수 있지만, 인간이 이해할 수 있는 코드는 실력 있는 프로그래머만 작성할 수 있다.

 

단순 비디오대여점에서 고객의 대여료 내역을 계산하고 출력하는 간단한 프로그램을 통해 리팩토링 기본을 배운다.

 

리팩토링 전 기본 코드
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();
 

 

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유