위 책을 보면서 정리한 글입니다.
많은 기능을 한 번에 처리하려고 할수록 문제가 발생햇을 때 해결하기 어렵다.
수 많은 매개변수 때문에 문제가 발생한 코드를 찾기가 어렵다.
매개변수가 많지만, 메서드 추출을 위한 기능이 명확하지 않을 때
- 매개변수가 많다는 것 : 메서드에서 필요한 값이 많다 --> 많은 기능을 처리하고 있을 '가능성'이 높다.
- 매개변수가 많다고 반드시 고쳐야 하는 것은 아니다
- 직관적으로 보이는 매개변수에서 개선에 대한 가능성을 생각해보고, 시도해서 개선 사항이 나타나지 않으면 과감하게 넘기는 것이 필요
메서드 기능이 혼재되어 있고, 매개변수가 많을 때 어떤 문제가 생겨서 수정이 필요할까
- 객체 지향의 다섯 원칙 중 하나인 '단일 책임의 원칙(SRP, The Single Responsibility Pirnciple)
- 메서드 기능이 혼재되어 있으면 한 기능이 수정이 필요할 때마다 메서드 내의 모든 기능에 대한 검증 테스트가 요구
- 메서드의 수정의 유연성이 떨어지며 많은 유지보수 비용이 발생
- 기능이 많아지면, 요구되는 매개변수가 늘어난다.
- 늘어난 매개변수는 메서드의 사용을 어렵게 하고, 제한함으로써 메서드의 재사용성을 떨어뜨린다.
- 메서드 호출시에도 인자가 모두 사용되지 않는 경우가 생긴다.
- 불필요한 인자의 전달과, 이에 대한 예외 코드가 필요해지며 코드를 복잡하게 만든다.
- 복잡해진 코드는 개발자의 실수로 인한 내재적인 오류를 포함할 가능성을 높인다.
- 복잡해진 코드의 기능을 명확히 보여주지 못하는 무분별한 매개변수
- 가독성이 떨어지며, 복잡하고 명확하지 못한 테스트 케이스를 발생시킨다.
public class TotalPaymentGate {
// 1. 객실 번호, 투숙 기간을 통한 예약 결제 기능 체크
// 2. 결제 방법에 따른 결제 진행
// 3. 결제 완료 후 예약 결제 정보 저장
// 전달 매개변수
/**
*
* @param payMethod // 결제 방법
* @param price // 결제 금액
* @param roomNum // 예약 객실 번호
* @param duringDay // 투숙 기간
* @param reservationName // 예약자명
* @param password // 예약 비밀번호
* @return // PayResultTO
*/
public PayResultTO requsetPay(String payMethod, int price, String roomNum, int duringDay, String reservationName, String password) {
DBManager dbManager = new DBManager();
// 1. roomNum, duringDay로 예약 결제 가능 체크
boolean isReservation = dbManger.checkRoomReservation(roomNum, duringDay);
PayResultTO payResult = null;
if(!isReservation) {
// 2. 결제 수단 객체 생성 및 결제 진행
Requestor requestor = RequstorFactory.createRequestor(payMethod);
Map<String, Object> result = requestor.requset(price, roomNum);
payResult = parsePayResultTO(payMethod, result);
// 3. 정상 결제 완료 후 reservationName, roomName, password로 예약 정보 저장
dbManager.saveRoomReservation(payMethod, price, roomNum, duringDay, reservationName, password);
}
return payResult;
}
}
위 코드의 문제점
ReservationChecker (예약 가능 체크 요청)
-- 전달 매개변수 : 예약 정보 객체, 객실 번호, 투숙 기간
-- 분리된 기능 : 객실 번호, 투숙 기간으로 예약 결제 가능 체크TotalPaymentGate (결제 요청)
-- 전달 매개변수 : 예약 정보 객체, 금액, 결제 정보
-- 분리된 기능 : 결제 방법에 따른 결제 진행ReservationStorage (예약 정보 저장 요청)
-- 전달 매개변수 : 예약 정보 객체, 금액, 결제 방법, 객실 번호, 투숙 기간, 예약 비밀번호, 예약자명
-- 분리된 기능 : 결제 완료 후 예약 결제 정보 저장
이전에는 한 기능이 변경되거나, 요구되는 매개변수가 변경되면 메서드 내의 모든 기능에 대한 검증이 필요했다.
개선 후, TotalPaymentGate 클래스는 결제 기능만 수행하고, 매개변수 또한 결제에 필요한 정보만 전달한다.
단위 테스트 미포함
- 예약 결제 가능 체크 기능에 대한 메서드 추출
- 예약 정보 기능에 대한 메서드 추출
단위 테스트 포함
- 추출할 메서드에 대한 단위 테스트 작성
- 추출 메서드에 해당하는 객체와 메서드 생성
- 기존 비즈니스 로직을 추출하여 이동
- 단위 테스트를 작동하여 통과 확인
public class ReservationChekcerTest extends TestCase {
@Test
public void testCheckReservatiopRoom() {
//Given
ReservationChecker reservationChecker = new ReservationChecker();
String roomNum = "2408";
int duringDay = 3;
//When
boolean isReservation = reservationChecker.checkReservationRoom(roomNum, duringDay);
//Then
assertEquals(isReservation, false);
}
}
public class ReservationChecker {
public boolean checkReservationRoom(String roomNum, int duringDay) {
DBManager dbManger = new DBManager();
// roomNum, duringDay로 예약 가능 체크
return dbManager.checkRoomReservation(roomNum, duringDay);
}
}
public class ReservationCheckerTest extends TestCase {
@Test
public void testSaveRoomReservation() {
//Given
ReservationStorage reservationStorage = new ReservationStorage();
String roomNum = "2408";
int duringDay = 3;
String payMethod = "Card";
int price = 200000;
String reservationName = 'James';
String password = "1234";
//When
boolean isSuccess = reservationStorage.saveRoomReservation(payMethod, price, roomNum, reservationName, passWord);
//Then
assertEqulas(isSuccess, true);
}
}
public class ReservationStorage {
public boolean saveRoomReservation(String payMethod, int price, String roomNum, String reservationName, String passWord) {
DBManager dbManager = new DBManager();
// 정상 결제 완료 후 reservationName, roomName, password로 예약 정보 저장
boolean isSuccess = dbManager.saveRoomReservation(payMethod, price, roomNum, duringDay, reservationName, password);
return isSuccess;
}
}
public class TotalPaymentGate {
// 결제 방법에 따른 결제 진행
// 전달 매개변수
/**
*
* @param payMethod // 결제 방법
* @param price // 결제 금액
* @return // PayResultTO
*/
public PayResultTO requsetPay(String payMethod, int price) {
DBManager dbManager = new DBManager();
PayResultTO payResult = null;
// 결제 수단 객체 생성 및 결제 진행
Requestor requestor = RequstorFactory.createRequestor(payMethod);
Map<String, Object> result = requestor.requset(price, roomNum);
payResult = parsePayResultTO(payMethod, result);
return payResult;
}
}
메서드 추출을 통한 개선의 이점