복잡해진 조건문을 단순하게 해결하기 위한 기술
같은 결과를 내는 조건문이 연속으로 있다면 하나의 조건식으로 통합
<변경 전>
double disabilityAmount() {
if (seniority < 2) {
return 0;
}
if (monthsDisabled > 12) {
return 0;
}
if (isPartTime) {
return 0;
}
}
<변경 후>
double disabilityAmount() {
if (isNotEligibleForDisability()) {
return 0;
}
}
boolean isNotEligibleForDisability() {
if (seniority < 2 && monthsDisabled > 12 && isPartTime)
return true;
}
중복 제어 흐름 코드 제거
조건문의 목적을 설명하는 이름을 가진 새 메소드에서 복잡한 표현식 분리 가능
조건문의 모든 분기에서 공통된 코드를 조건문 밖으로 이동
복잡한 조건문(if-then-else)을 조건부분, then부분을 메소드로 추출
이미지 속 코드가 너무 생략되어서 "이게 왜 이렇게 되는 거지?"싶을까봐 예제를 가져왔다.
<변경 전>
class Simple_Example{
void Example(){
if(date < SUPPER_STAR || date > SUPPER_END)
charge = quantity * winterRate + winterServiceCharge;
else
Charge = quantity * summerRate;
}
}
<변경 후>
class Simple_Example{
void Example(){
if(IsNotSummer())
charge = CalculateWinterRate();
else
Charge = CalculateSummerRate());
}
}
private bool IsNotSummer(){
return date < SUPPER_STAR || date > SUPPER_END;
}
private bool CalculateWinterRate(){
return quantity * winterRate + winterServiceCharge;
}
private bool CalculateSummerRate(){
return quantity * summerRate;
}
객체의 타입에 따라서 다른 행동을 하는 조건이 있다면 각 조건의 내용을 서브 클래스의 메소드로 오버라이딩하고 원본 메소드는 추상 메소드로 변경
<변경 전>
class Bird {
// ...
double getSpeed() {
switch (type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
throw new RuntimeException("Should be unreachable");
}
}
<변경 후>
abstract class Bird {
// ...
abstract double getSpeed();
}
class European extends Bird {
double getSpeed() {
return getBaseSpeed();
}
}
class African extends Bird {
double getSpeed() {
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
double getSpeed() {
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
}
// Somewhere in client code
speed = bird.getSpeed();
TDA 원칙을 준수
TDA 원칙 (Tell, don't ask 원칙)
"물어보지 말고 그냥 시켜라"
객체와 객체가 협력하는 경우, 다른 객체에게 정보를 요구하지 말고 그냥 행동하도록 시키라는 의미
정보 은닉의 중요성을 강조하는 원칙
중복 코드 제거
거의 동일한 조거문을 많이 제거하므로
개방 폐쇄 원칙을 준수
새로운 실행 조건을 추가해야 하는 경우 기존 코드를 건드리지 않고 새 하위 클래스 생성
상태를 기록하고 처리의 흐름을 제어하기 위한 Boolean형 변수 제거
break, continue, return 을 대신 사용
실행 경로를 알기 힘든 조건문 대신 각 조건별로 보호 구문(guard clause) 사용
null 값을 반복해서 체크한다면 null 값 대신 null 객체 사용
특정 부분의 코드가 프로그램의 상태를 가정한다면 그 가정을 명확한 Assert의 형태로 변경
Assertion
프로그램의 특정 지점에 위치한 어써션은 해당 지점에서 개발자가 반드시 참(true)이어야 한다고 생각하는 사항을 표현한 논리식
어써션이 위반되는 경우(논리식이 거짓)는 프로그램에 버그나 문제가 있는 것을 암시
<변경 전>
double getExpenseLimit() {
// Should have either expense limit or
// a primary project.
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit :
primaryProject.getMemberExpenseLimit();
}
<변경 후>
double getExpenseLimit() {
Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
📑 참고 자료
- https://refactoring.guru/refactoring
- https://wikidocs.net/book/55
- http://dj6316.torchpad.com/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%28refactoring%29/CH.09+%EC%A1%B0%EA%B1%B4%EB%AC%B8+%EA%B0%84%EA%B2%B0%ED%99%94/29.%EC%A1%B0%EA%B1%B4%EB%AC%B8+%EC%AA%BC%EA%B0%9C%EA%B8%B0+Decompose+Conditional
- https://effectiveprogramming.tistory.com/entry/Tell-dont-ask
- https://blog.daum.net/question0921/1106