티스토리 뷰
조건문 쪼개기
복잡한 조건문이 있을 경우 각 부분을 메서드 추출 기법을 통해 빼내자
💡 동기 및 예제 코드
프로그래밍에서 복잡한 부분은 주로 복잡한 조건문을 파악하는 일입니다. 조건을 검사하고 다양한 조건에 따라 다른 작업을 처리하는 코드를 작성하다 보면 금방 메서드가 길어집니다. 이런 장황한 메서드는 잘게 쪼개고 용도에 맞는 이름을 가지는 메서드 추출기법을 통해 정리를 하면 가독성을 높일 수 있습니다.
if (date.isBefore(SUMMER_START) && date.isAfter(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
} else {
charge = quantity * summerRate;
}
💡 리팩토링된 코드
아래의 상황에서는 메서드 추출 기법을 사용하더라도 이득이 안될수 있지만 메서드의 길이가 길고 조건문이 복잡해지면 차이가 많이 발생하게 됩니다.
if (notSummer(date)) {
charge = winterCharge(quantity);
} else {
charge = summerCharge(quantity);
}
private boolean notSummer(LocalDate date) {
return date.isBefore(SUMMER_START) && date.isAfter(SUMMER_END);
}
private double summerCharge(int quantity) {
return quantity * summerRate;
}
private double winterCharge(int quantity) {
return quantity * winterRate + winterServiceCharge;
}
중복 조건식 통합
여러 조건의 결과가 동일한 경우 하나의 조건문으로 합친후 메서드 추출 기법을 사용할 수 있습니다.
💡 동기 및 예제 코드
조건식 통합 리팩토링을 실시하면 메서드 추출 기법을 적용할 수 있는 기반을 마련할 수 있으며, 이 과정에서 목적을 표현하는 메서드명을 통해 내부 구현을 살펴보지 않더라도 코드를 읽을 수 있습니다.
public double disabilityAmount() {
if (seniority < 2) return 0;
if (monthsDisabled > 12) return 0;
if (isPartTime) return 0;
return 1;
}
💡 리팩토링된 코드
public double disabilityAmount() {
if (isNotEligibleForDisability()) return 0;
return 1;
}
public boolean isNotEligibleForDisability() {
return seniority < 2 || monthsDisabled > 12 || isPartTime;
}
조건문의 공통 실행 코드 빼내기
조건문의 모든 절에 같은 실행 코드가 있는 경우 같은 부분을 조건문 밖으로 빼낼 수 있습니다.
💡 동기 및 예제 코드
공통으로 실행되는 코드가 조건문의 앞절에 있는 경우 조건문의 앞으로 뺍니다.
공통으로 실행되는 코드가 조건문의 끝절에 있는 경우 조건문의 뒤로 뺍니다.
공통으로 실행되는 코드가 중간에 있는 경우 앞뒤의 코드와 위치를 변경해도 되는지 파악한 후 앞이나 뒤로 뺍니다.
예외처리에서도 이러한 방식을 적용할 수 있는데 코드가 try 구간과 모든 catch 구간 안에 있다면 final 구간으로 옮길 수 있습니다.
// 수정 전
public void method() {
if (isSpecialDeal()) {
total = price * 0.95;
send();
} else {
total = price * 0.98;
send();
}
}
// 수정 후
public void method() {
if (isSpecialDeal()) {
total = price * 0.95;
} else {
total = price * 0.98;
}
send();
}
제어 플래그 제거
제어 플래그의 역할을 하는 변수가 있는 경우 그 변수를 break, return문으로 변경하자
💡 동기 및 예제 코드
제어 플래그가 무조건적으로 나쁘다고 할 수는 없습니다. 다만 불필요한 경우 사용을 자제하는게 가독성측면에서 좋은거 같습니다.
public static boolean find(int[] data, int target) {
boolean flag = false;
for (int i = 0; i < data.length; i++) {
if (!flag) {
if (data[i] == target) {
flag = true;
}
}
}
return flag;
}
💡 리팩토링된 코드
// 임시변수 result 사용과 break 사용
public static boolean findA(int[] data, int target) {
boolean result = false;
for (int i = 0; i < data.length; i++) {
if (data[i] == target) {
result = true;
break;
}
}
return result;
}
public static boolean find(int[] data, int target) {
for (int i = 0; i < data.length; i++) {
if (data[i] == target) {
return true;
}
}
return false;
}
여러 겹의 조건문을 감시절로 전환
메서드에 조건문이 있어서 정상적인 실행 경로를 파악하기 힘든 경우에는 모든 특수한 경우에 감시절을 사용합니다.
💡 예제 코드
사망 직원, 해고 직원, 은퇴 직원의 경우 특수한 규칙이 적용되는 급여 정산 시스템의 일부입니다.
// 수정 전
public double getPayAmount() {
double result;
if (isDead) {
result = deadAmount();
} else if (isSeparated) {
result = separatedAmount();
} else if (isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
return result;
}
// 수정 후
public double getPayAmount() {
if (isDead) return deadAmount();
if (isSeparated) return separatedAmount();
if (isRetired) return retiredAmount();
return normalPayAmount();
}
조건문을 재정의로 전환
객체의 타입에 따라 다른 기능을 실행하는 조건문이 있는 경우 다형성을 통해 이를 극복할 수 있습니다.
💡 예제 코드
Employee의 종류에는 3가지가 있습니다. 각기 다르게 금액을 계산하고 있습니다. 이때 새로운 타입이 추가될 때마다 switch문에 변경이 발생하게 됩니다. 이를 다형성이라는 강력한 방법을 사용해 개선할 수 있습니다.
public class Employee {
private int monthlySalary; // 봉급
private int commission; // 수수료
private int bonus; // 보너스
private EmployeeType employeeType;
public int payAmount() {
switch (employeeType) {
case ENGINEER:
return monthlySalary;
case SALESMAN:
return monthlySalary + commission;
case MANAGER:
return monthlySalary + bonus;
default:
throw new IllegalStateException("없는 사원입니다.");
}
}
}
💡 리팩토링된 코드
다형성을 통해 OCP를 지킬 수 있습니다. 또한 관리해야하는 클래스는 늘어나지만 각각의 객체에게 적절한 책임을 부여할 수 있습니다.
public interface Employee {
int payAmount();
}
@AllArgsConstructor
public class Engineer implements Employee {
private int monthlySalary; // 봉급
private int commission; // 수수료
private int bonus; // 보너스
@Override
public int payAmount() {
return monthlySalary;
}
}
@AllArgsConstructor
public class Manager implements Employee {
private int monthlySalary; // 봉급
private int commission; // 수수료
private int bonus; // 보너스
@Override
public int payAmount() {
return monthlySalary + bonus;
}
}
@AllArgsConstructor
public class Salesman implements Employee {
private int monthlySalary; // 봉급
private int commission; // 수수료
private int bonus; // 보너스
@Override
public int payAmount() {
return monthlySalary + commission;
}
}
// 테스트
public static void main(String[] args) {
Employee engineer = new Engineer(100, 200, 300);
Employee salesman = new Salesman(100, 200, 300);
Employee manager = new Manager(100, 200, 300);
engineer.payAmount(); // 100
salesman.payAmount(); // 300
manager.payAmount(); // 400
}
- Total
- Today
- Yesterday
- polling publisher spring boot
- transactional outbox pattern
- java userThread와 DaemonThread
- java ThreadLocal
- spring boot redisson destributed lock
- spring boot redisson 분산락 구현
- spring boot poi excel download
- pipeline architecture
- 자바 백엔드 개발자 추천 도서
- microkernel architecture
- spring boot 엑셀 다운로드
- spring boot redis 대기열 구현
- redis sorted set
- 서비스 기반 아키텍처
- redis sorted set으로 대기열 구현
- @ControllerAdvice
- transactional outbox pattern spring boot
- 람다 표현식
- space based architecture
- spring boot excel download oom
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- spring boot excel download paging
- pipe and filter architecture
- redis 대기열 구현
- 공간 기반 아키텍처
- 트랜잭셔널 아웃박스 패턴 스프링부트
- service based architecture
- 레이어드 아키텍처란
- spring boot redisson sorted set
- JDK Dynamic Proxy와 CGLIB의 차이
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |