Refactoring#9
-
Upload
seungmin-yu -
Category
Technology
-
view
175 -
download
0
Transcript of Refactoring#9
Decompose Conditional(조건문 쪼개기)
복잡한 조건문(if-then-else)이 있을 때는 if, then, else 부분을 가각 메서드로 빼내자.
if 절을 별도의 메서드로 빼내자.
then 절과 else 절을 각각의 메서드로 빼내자.
If����������� ������������������ (date.before(SUMMER_START)����������� ������������������ ||����������� ������������������ date.after(SUMMER_END))����������� ������������������ ����������� ������������������ charge����������� ������������������ =����������� ������������������ quantity����������� ������������������ *����������� ������������������ _winterRate����������� ������������������ +����������� ������������������ _winterServiceCharge;����������� ������������������ else����������� ������������������ ����������� ������������������ charge����������� ������������������ =����������� ������������������ quantity����������� ������������������ *����������� ������������������ _summerRate;
If����������� ������������������ (notSummer(date))����������� ������������������ ����������� ������������������ charge����������� ������������������ =����������� ������������������ winterCharge(quantity);����������� ������������������ else����������� ������������������ ����������� ������������������ charge����������� ������������������ =����������� ������������������ summerCharge(quantity);
Consolidate Conditional Expression(중복 조건식 통합)
여러 조건 검사식의 결과가 같을 때는 하나의 조건문으로 합친 후 메서드로 빼내자.
모든 조건문에 부작용이 없는지 검사하자.
하나라도 부작용이 있으면 X
여러 개의 조건문을 논리 연산자를 사용해 하나의 조건문으로 바꾸자.
합친 조건문에 메서드 추출 적용을 고려하자.
double����������� ������������������ disabilityAmount()����������� ������������������ {����������� ������������������ ����������� ������������������ if����������� ������������������ (_seniority����������� ������������������ <����������� ������������������ 2)����������� ������������������ return����������� ������������������ 0;����������� ������������������ ����������� ������������������ if����������� ������������������ (_monthDisabled����������� ������������������ >����������� ������������������ 12)����������� ������������������ return����������� ������������������ 0;����������� ������������������ ����������� ������������������ if����������� ������������������ (_isPartTime)����������� ������������������ return����������� ������������������ 0;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ …⋯
double����������� ������������������ disabilityAmount()����������� ������������������ {����������� ������������������ ����������� ������������������ if����������� ������������������ (isNotEligableForDisability())����������� ������������������ return����������� ������������������ 0;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ …⋯
Consolidate Duplicate Conditional Fragments(조건문의 공통 실행 코드 빼기)조건문의 모든 절에 같은 실행 코드가 있을 때는 같은 부분을 조건문 밖으로 빼자.
조건에 상관없이 공통적으로 실행되는 코드를 찾자.
공통 코드가 조건문의 앞 절에 있을 땐 조건문 앞으로 빼자.
공통 코드가 조건문의 끝 절에 있을 땐 조건문 뒤로 빼자.
공통 코드가 조건문의 중간 절에 있을 땐 앞뒤의 코드와 위치를 바꿔도 되는지 판단하자. 그래서 바꿔도 된다면 조건문의 앞이나 끝 절로 뺀 후 앞의 단계처럼 조건문의 앞이나 뒤로 빼자.
공통 코드 명령이 둘 이상일 땐 메서드로 만들자.
if����������� ������������������ (isSpecialDeal())����������� ������������������ {����������� ������������������ ����������� ������������������ total����������� ������������������ =����������� ������������������ price����������� ������������������ *����������� ������������������ 0.95;����������� ������������������ ����������� ������������������ send();����������� ������������������ }����������� ������������������ else����������� ������������������ {����������� ������������������ ����������� ������������������ total����������� ������������������ =����������� ������������������ price����������� ������������������ *����������� ������������������ 0.98;����������� ������������������ ����������� ������������������ send();����������� ������������������ }
if����������� ������������������ (isSpecialDeal())����������� ������������������ ����������� ������������������ total����������� ������������������ =����������� ������������������ price����������� ������������������ *����������� ������������������ 0.95;����������� ������������������ else����������� ������������������ ����������� ������������������ total����������� ������������������ =����������� ������������������ price����������� ������������������ *����������� ������������������ 0.98;����������� ������������������ send();����������� ������������������
Remove Control Flag(제어 플래그 제거)
논리 연산식의 제어 플래그 역할을 하는 변수가 있을 때는 그 변수를 break 문이나 return 문으로 바꾸자.
break 문이나 continue 문이 있을 때
논리문을 빠져나오게 하는 제어 플래그 값을 찾자.
그 제어 플래그 값을 대입하는 코드를 break 문이나 continue 문으로 바꾸자.
break 문이나 continue 문이 없는 언어에서
로직을 메서드로 빼내자.
논리문을 빠져나오게 하는 제어 플래그 값을 찾자.
빠져나오게 하는 값을 return 문으로 바꾸자.
void����������� ������������������ checkSecurity(String[]����������� ������������������ people)����������� ������������������ {����������� ������������������ ����������� ������������������ boolean����������� ������������������ found����������� ������������������ =����������� ������������������ false;����������� ������������������ ����������� ������������������ for����������� ������������������ (int����������� ������������������ i����������� ������������������ =����������� ������������������ 0;����������� ������������������ i����������� ������������������ <����������� ������������������ people.length;����������� ������������������ i++)����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ if����������� ������������������ (!found)����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ if����������� ������������������ (people[i].equals(“Don”))����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ sendAlert();����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ found����������� ������������������ =����������� ������������������ true;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ if����������� ������������������ (people[i].equals(“John”))����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ sendAlert();����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ found����������� ������������������ =����������� ������������������ true;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ }����������� ������������������ }
void����������� ������������������ checkSecurity(String[]����������� ������������������ people)����������� ������������������ {����������� ������������������ ����������� ������������������ for����������� ������������������ (int����������� ������������������ i����������� ������������������ =����������� ������������������ 0;����������� ������������������ i����������� ������������������ <����������� ������������������ people.length;����������� ������������������ i++)����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ if����������� ������������������ (people[i].equals(“Don”)����������� ������������������ ||����������� ������������������ people[i].equals(“John”))����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ sendAlert();����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ break;����������� ������������������ ����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ }����������� ������������������ }
TODO: try one more step
Replace Nested Conditional with Guard Clauses(여러 겹의 조건문을 감시 절로 전환)
메서드에 조건문이 있어서 정상적인 실행 경로를 파악하기 힘들 땐 모든 특수한 경우에 감시 절을 사용하자.
조건 절마다 감시 절을 넣자.
그 감시 절은 값을 반환하거나 예외를 통지한다.
각 조건 절을 감시 절로 바꿀 때마다 컴파일과 테스트를 실시하자.
모든 감시 절의 결과가 같다면 중복 조건식 통합 기법을 실시하자.
double����������� ������������������ getPayAmount()����������� ������������������ {����������� ������������������ ����������� ������������������ double����������� ������������������ result;����������� ������������������ ����������� ������������������ if����������� ������������������ (_isDead)����������� ������������������ result����������� ������������������ =����������� ������������������ deadAmount();����������� ������������������ ����������� ������������������ else����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ if����������� ������������������ (_isSeparated)����������� ������������������ result����������� ������������������ =����������� ������������������ separatedAmount();����������� ������������������ ����������� ������������������ ����������� ������������������ else����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ if����������� ������������������ (_isRetired)����������� ������������������ result����������� ������������������ =����������� ������������������ retiredAmount();����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ else����������� ������������������ result����������� ������������������ =����������� ������������������ normalAmount();����������� ������������������ ����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ return����������� ������������������ result;����������� ������������������ }
double����������� ������������������ getPayAmount()����������� ������������������ {����������� ������������������ ����������� ������������������ if����������� ������������������ (_isDead)����������� ������������������ return����������� ������������������ deadAmount();����������� ������������������ ����������� ������������������ if����������� ������������������ (_isSeparated)����������� ������������������ return����������� ������������������ separatedAmount();����������� ������������������ ����������� ������������������ if����������� ������������������ (_isRetired)����������� ������������������ return����������� ������������������ retiredAmount();����������� ������������������ ����������� ������������������ return����������� ������������������ normalAmount();����������� ������������������ }
Replace Conditional with Polymorphism(조건문을 재정의로 전환)객체 타입에 따라 다른 기능을 실행하는 조건문이 있을 때는 조건문의 각 절을 하위클래스의 재정의 메서드 안으로 옮기고, 원본 메서드는 abstract 타입으로 수정하자.
조건문이 메서드 코드의 일부라면 그 조건문을 따로 떼서 메서드 추출을 적용하자
필요하다면 메서드 이동을 적용해서 조건문을 최상위 클래스로 옮기자.
하위클래스 중 하나를 택해서 그 안에 조건문 메서드를 재정의하는 메서드를 작성하자. 조건문의 해당 절에 있는 코드를 그 하위클래스 메서드로 옮기고 적절히 수정하자.
메서드로 복사해 넣은 조건문 안의 절은 삭제하자.
조건문의 나머지 절의 코드도 마찬가지로 하위클래스 메서드 안으로 옮기자.
상위클래스 메서드를 abstract 타입으로 만들자.
class����������� ������������������ Employee…⋯����������� ������������������ {����������� ������������������ ����������� ������������������ int����������� ������������������ payAmount()����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ switch����������� ������������������ (getType())����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ case����������� ������������������ EmployeeType.ENGINEER:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ return����������� ������������������ _monthlySalary;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ case����������� ������������������ EmployeeType.SALESMAN:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ return����������� ������������������ _monthlySalary����������� ������������������ +����������� ������������������ _commission;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ case����������� ������������������ EmployeeType.MANAGER:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ return����������� ������������������ _monthlySalary����������� ������������������ +����������� ������������������ _bonus;����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ default:����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ throw����������� ������������������ new����������� ������������������ RuntimeException(“Exception”);����������� ������������������ ����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ }����������� ������������������ }
class����������� ������������������ Employee…⋯����������� ������������������ {����������� ������������������ ����������� ������������������ int����������� ������������������ payAmount()����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ return����������� ������������������ _type.payAmount(this);����������� ������������������ ����������� ������������������ }����������� ������������������ }����������� ������������������ class����������� ������������������ EmployeeType…⋯����������� ������������������ {����������� ������������������ ����������� ������������������ abstract����������� ������������������ int����������� ������������������ payAmount(Employee����������� ������������������ emp);����������� ������������������ }����������� ������������������ class����������� ������������������ Engineer…⋯����������� ������������������ class����������� ������������������ Salesman…⋯
Introduce Null Object(Null 검사를 널 객체에 위임)
Null 값을 검사하는 코드가 계속 나올 때는 null 값을 널 객체로 만들자.
원본 클래스 안에 널 객체 역할을 할 하위클래스를 작성하자. 원본 클래스와 널 클래스 안에 isNull 메서드를 작성하자. 원본 클래스의 isNull 메서드는 false 를 반환해야 하고, 널 클래스의 isNull 메서드는 true 를 반환해야 한다.
원본 객체에 요청하면 null 을 반환할 수 있는 부분을 전부 찾자. 그래서 그 부분을 널 객체로 바꾸자.
원본 타입의 변수를 null 과 비교하는 코드를 전부 찾아서 isNull 호출로 바꾸자.
클라이언트가 null 이 아니면 한 메서드를 호출하고 null 이면 다른 메서드를 호출하는 case 문을 전부 찾자.
각 case 문마다 널 클래스 안의 해당 메서드를 다른 기능의 메서드로 재정의하자.
재정의 메서드를 사용하는 부분에서 조건문을 삭제하고 컴파일과 테스트를 실시하자.
if����������� ������������������ (customer����������� ������������������ ==����������� ������������������ null)����������� ������������������ plan����������� ������������������ =����������� ������������������ BillingPlan.basic();����������� ������������������ else����������� ������������������ plan����������� ������������������ =����������� ������������������ customer.getPlan();
plan����������� ������������������ =����������� ������������������ customer.getPlan();
class����������� ������������������ Customer����������� ������������������ {����������� ������������������ ����������� ������������������ static����������� ������������������ Customer����������� ������������������ newNull()����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ return����������� ������������������ new����������� ������������������ NullCustomer();����������� ������������������ ����������� ������������������ }����������� ������������������ ����������� ������������������ BillingPlan����������� ������������������ getPlan()����������� ������������������ {����������� ������������������ ����������� ������������������ ����������� ������������������ …⋯����������� ������������������ ����������� ������������������ }����������� ������������������ }����������� ������������������ …⋯����������� ������������������ Customer����������� ������������������ getCustomer()����������� ������������������ {����������� ������������������ ����������� ������������������ return����������� ������������������ (_customer����������� ������������������ ==����������� ������������������ null)����������� ������������������ ?����������� ������������������ ����������� ������������������ ����������� ������������������ Customer.newNull()����������� ������������������ :����������� ������������������ ����������� ������������������ ����������� ������������������ _customer;����������� ������������������ }����������� ������������������ …⋯����������� ������������������ class����������� ������������������ NullCustomer����������� ������������������ extends����������� ������������������ Customer����������� ������������������ {����������� ������������������ …⋯����������� ������������������
Introduce Assertion(어설션 넣기)
일부 코드가 프로그램의 어떤 상태를 전제할 때는 어설션을 넣어서 그 전제를 확실하게 코드로 작성하자.
어설션 기능을 사용할 수 있는 Assert 클래스를 작성하자.
double����������� ������������������ getExpenseLimit()����������� ������������������ {����������� ������������������ ����������� ������������������ //����������� ������������������ 비용����������� ������������������ 한도를����������� ������������������ 두던지,����������� ������������������ 주요����������� ������������������ 프로젝트를����������� ������������������ 정해야����������� ������������������ 한다.����������� ������������������ ����������� ������������������ return����������� ������������������ (_expenseLimit����������� ������������������ !=����������� ������������������ NULL_EXPENSE)����������� ������������������ ?����������� ������������������ ����������� ������������������ ����������� ������������������ _expenseLimit����������� ������������������ :����������� ������������������ ����������� ������������������ ����������� ������������������ _primaryProject.getMemberExpenseLimit();����������� ������������������ }
double����������� ������������������ getExpenseLimit()����������� ������������������ {����������� ������������������ ����������� ������������������ Assert.isTrue(_expenseLimit����������� ������������������ !=����������� ������������������ NULL_EXPENSE����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ����������� ������������������ ||����������� ������������������ _primaryProject����������� ������������������ !=����������� ������������������ null);����������� ������������������ ����������� ������������������ return����������� ������������������ (_expenseLimit����������� ������������������ !=����������� ������������������ NULL_EXPENSE)����������� ������������������ ?����������� ������������������ ����������� ������������������ ����������� ������������������ _expenseLimit����������� ������������������ :����������� ������������������ ����������� ������������������ ����������� ������������������ _primaryProject.getMemberExpenseLimit();����������� ������������������ }