Refactoring tutorial

1,587 views

Published on

0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,587
On SlideShare
0
From Embeds
0
Number of Embeds
29
Actions
Shares
0
Downloads
56
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Refactoring tutorial

  1. 1. 리팩토링 Tutorial SEEG
  2. 2. 목차 리팩토링 개요 클래스 내부의 냄새  측정할 수 있는 냄새  이름  불필요한 복잡성  중복  조건 로직 클래스 사이의 냄새  데이터  상속  책임  변경수용하기  라이브러리 클래스 프로그램 리팩토링
  3. 3. 리팩토링 개요3 Confidential 12/13/2011
  4. 4. 리팩토링 배경: 리팩토링 이란? Bad Code Good Code 리팩토링
  5. 5. 리팩토링 배경:: Bad Code가 야기시키는 문제점 Broken Window Theory 나쁜코드에 의한 생산성 저하는 계속 악화됨
  6. 6. 리팩토링 정의  명사형 정의  “소프트웨어를 보다 쉽게 이해할 수 있고, 적은 비용으로 수정할 수 있도록 겉으로 보이는 동작의 변화 없이 내부 구조를 변 경하는 것”  동사형 정의  “일련의 리팩토링을 적용하여 겉으로 보이는 동작의 변화 없이 소프트웨어의 구조를 바꾼다.”  다음 방법으로 동작의 변경 여부 검증  테스팅  최대한 개발 Tool의 지원 기능 사용  아주 조심히 진행  Refactoring의 예public int getCharge(int quantity) { public int getCharge(int quantity) { int charge = 0; int charge = 0; Date now = new Date(); Date now = new Date(); // 현재 Summer타임 기간이라면 if (isSummerTime(now)) { if (now.before(SUMMER_START)|| now.after(SUMMER_END)) { charge = quantity * WINTER_RATE + charge = quantity * WINTER_RATE + WINTER_SERVICEC_CHARGE; WINTER_SERVICEC_CHARGE; } else { } else { charge = quantity * SUMMER_RATE; charge = quantity * SUMMER_RATE; } } return charge; return charge } } private boolean isSummerTime(Date now) { return now.before(SUMMER_START) || now.after(SUMMER_END); }
  7. 7. 리팩토링 원리: 왜 리팩토링을 해야 하는가? 프로그램을 빨리 작성하도록 도와줌 어떻게?  리팩토링은 디자인을 개선시켜 줌 디자인이 나쁜 코드는?  같은 작업을 위해 더 많은 코드 사용  중복이 많고 이해하기 어려움  수정 시 어려움으로 나타남 리팩토링은 소프트웨어를 더 이해하기 쉽게 만듬  리팩토링된 코드는 숨겨진 사용자인 유지보수자의 이해를 도움  낮선 코드에 대해 리팩토링 하면서 코드에 대해 이해하기도 함
  8. 8. 리팩토링 원리: 언제 리팩토링을 해야 하는가? 틈틈이 계속  리팩토링 자체가 목적이 아니라, 다른 것을 하기 위해 리팩토링 을 하는 것이고, 리팩토링은 그 다른 것을 하는데 도움을 준다. 삼진 규칙  세 번째로 비슷한 것을 하게 되면 리팩토링을 한다 기능을 추가할 때, 버그를 수정할 때  코드에 대한 이해를 돕기 위해서 코드 검토 시  고수준의 의견을 얻을 수 있음  준비가 많이 필요함
  9. 9. 리팩토링 원리 : 두 개의 모자 기능 추가 리팩토링 기존 코드 수정 없이 새로운 기능 추가  기능 추가 없이 코드 구조 수정 테스트 추가  테스트 추가하지 않아야 함 테스트 정상 동작 확인  테스트 정상 동작 확인 개발할 때에는 모자를 자주 바꿔 쓰고(10분~30분), 한 시점에는 하나의 모자만 써야 한다.
  10. 10. 리팩토링 원리: 리팩토링을 할 때의 문제 데이터 베이스와 연관된 코드의 리팩토링 인터페이스의 변경  Publish 된 인터페이스의 이름 및 파라미터에 대한 리팩토링  Ex) Android의 Service.onStart() 메소드  새로운 I/F를 만들고 Delegation 사용  예방책 : 애매한 메소드는 API에 넣지 말자  Publish 된 인터페이스의 Throw절의 변경  이 경우 Delegation 사용 불가  예방책 : Super Exception 사용 리팩토링이 어려운 디자인 변경  디자인에 실수가 있어 마음대로 리팩토링 할 수 없을 때  어떤 디자인 결정 사항이 너무 중요해서 리팩토링을 기대할 수 없는 경우  보안 문제, 퍼포먼스 문제 언제 리팩토링을 하지 말아야 하는가?  코드를 처음부터 작성 하는 게 나을 정도로 엉망인 경우  현재의 코드가 작동하지 않을 경우  마감일에 가까울 경우
  11. 11. 리팩토링 원리: 사전 디자인과 리팩토링의 효용성 관계 “디자인으로는 생각을 아주 빨리 할 수 있지만, 그 생각에는 여 기저기 작은 구멍이 뚫려있다.”  “With design I can think very fast, but my thinking is full of little holes” – Alistair Cockburn 리팩토링에 의해 사전 디자인의 Role이 변경됨  완벽한 사전 디자인이 아닌 적절한 솔루션을 제공하는 디자인이면 충분  리팩토링을 통해 변경에 많은 비용이 들지 않기 때문  따라서, 디자인이 단순화 됨 디자인 중심의 단점  설계 비용이 많이 든다.  복잡해 진다.  유지보수 비용이 상대적으로 많이 든다.  중복에 의한게 아닌 Complexity 때문
  12. 12. 리팩토링 원리: 리팩토링과 퍼포먼스 리팩토링은 장기적으로 최적화 단계에 소프트웨어의 튜닝을 돕는다.  리팩토링을 통해 퍼포먼스 튜닝에 할당할 수 있는 시간을 얻을 수 있음.  프로그램이 잘 분해되어 있으면, 튜닝을 위한 분석 시 좀 더 세밀한 분석이 가능하다.
  13. 13. 냄새 냄새(코드냄새)는 코드에 잠재된 문제에 대한 경고 표 시 리팩토링을 해야 하는 문제점 표시 대표적 냄새  중복코드  부적절한 이름  기능에 대한 욕심  부적절한 친밀  주석  긴 메소드  긴 파라미터 리스트  Switch문
  14. 14. 냄새: 중복 코드 가장 많이 발생하는 냄새 두 가지 중복이 있음  Obvious 중복  보통 Copy & Paste에 의해 발생  Unobvious 중복  계층구조의 병렬적 구현  비슷한 알고리즘(ex, 문자열, 도메인 특화 로직) 해결책  하나의 클래스에서 중복  ExtractMethod  두 형제 클래스에서 중복  ExtractMethod -> Pull Up Field or Pull Up Method -> Form Template Method
  15. 15. 냄새: 부적절한 이름 잘못된 이름은 코드의 이해를 방해한다. “People often make assumptions based on the object names alone” – Word Cunningham 해결책  Rename Method  Rename Field  Rename Constants Tip !  일관성 없는 상세이름 보다는 일관성 있는 광의의 이름이 적합  add, register, put, create -> add  일관성 있는 이름을 위해서 용어 사전은 필수적
  16. 16. 냄새: 기능에 대한 욕심 특정 클래스 내의 메소드가 동 PriceCalculator Product 작을 위해 다른 클래스에 있는 .. calculate() .. getQuantity() 정보를 많이 필요로 하는 경우 bmethod() getDiscountRate() 해결책  Move Method
  17. 17. 냄새: 부적절한 친밀 특정 클래스 내의 메소드가 다른 Class A Class B 클래스에 있는 메소드(private 이 .. .. 어야 할)를 더 많이 사용하는 경우 amethod() xmethod() bmethod() ymethod() 해결책  두 개의 독립적인 클래스가 엉켜 있다 면  Move Method  서로를 가리키고 있다면  Change Bidirectional Reference to Unidirectional  중재하는 클래스가 없기 때문에 엉켜 있다면  Extract Hierarchy-> Hide Delegate
  18. 18. 냄새: 주석 주석은 대부분 코드가 명확 치 않다고 판단될 때 쓰임 주석은 나쁜 코드를 유도함 해결책  코드의 일정 블록 설명  Extract Method  메소드가 하는 일 설명  Rename Method  선 조건 설명  Introduce Assertion
  19. 19. 냄새: 긴 메소드 긴 메소드는 코드를 이해하기 힘들게 만든다. 주석을 달아야 할 필요를 느낄 때 마다 메소드로 분리 해결책  Extract Method 주의점  코드의 이해를 돕기 위해서는 코드의 의도를 잘 나타내는 이름 지어야  부적절한 이름을 가진 짧은 메소드 집합은 긴 메소드보다 이해하 기 힘들다.
  20. 20. 냄새: 긴 파라미터 리스트 객체지향 코드에서는 파라미터가 많을 필요 없다. 문제점  이해하기 어려움  일관성 없음  변경이 많이 됨 해결책  이미 알고 있는 다른 객체로부터 값 얻어올 수 있음  Replace Parameter with Method  매개변수가 하나의 Object로 부터 나오면  Preserve Whole Object  매개변수 데이터가 하나의 논리 객체로 부터 얻어지는게 아닐 경우  Introduce Parameter Object 주의점  Caller와 Callee간의 데이터 종속성을 만들고 싶지 않을 경우 리팩토링을 피해야함
  21. 21. 냄새: Switch 문 Switch문은 본질적으로 중복의 문제를 발생  타입 추가 시, 관련된 모든 Switch문을 수정해야 함 좋은 OOP코드의 특징은 Switch문이 비교적 적음 해결책  동일한 조건에 대한 Switch문이 여러 곳에서 사용됨  Step 1: Extract Method를 통해 각 조건 절에서 코드 추출  Step 2: Move Method를 통해 연관된 코드를 옳바른 클래스로 옮김  Step 3: Replace Type Code with Subclass or Replace Type Code with State/Strategy 를 통해 상속구조 만듬 주의  Switch문의 중복이 많지 않다면 바꿀 필요 없음 21 Confidential 12/13/2011
  22. 22. 결론 리팩토링은 코드를 이해하기 쉽게 만든다. 리팩토링은 목표가 아니라, 다른 작업을 하기 위한 과 정이다. 리팩토링은 틈틈히 계속 해야 한다. 22 Confidential 12/13/2011
  23. 23. 클래스 내부의 냄새23 Confidential 12/13/2011
  24. 24. 측정할 수 있는 냄새24 Confidential 12/13/2011
  25. 25. 측정할 수 있는 냄새: 개요 특징  찾기 쉽다. 관련 냄새  주석  긴 메소드  거대한 클래스  긴 매개변수 리스트
  26. 26. 측정할 수 있는 냄새: 주석 항목 설명 징후 • 주석기호가 코드에 나타남 -> 주석은 향후 업데이트에서 누락될 여지가 크기 때문에 잘못 된 정보를 나타낼 가능성 이큼 원인 • 코드가 명확 치 않다고 판단될 때 쓰임 - 알고리즘 또는 개발 방식에 대한 설명을 위해 쓰임 해야 할 일 • 일정 블록 설명 -> Extract Method • 메소드가 하는 일 설명 -> Rename Method • 선조건 설명 -> Introduce Assertion 효과 • 의사소통 증진 • 중복 노출 금기 자기 역할을 다하는 주석 삭제하지 말아야 함
  27. 27. 측정할 수 있는 냄새: 주석 - 예제코드package ch3;public class Matcher { public Matcher() { } 메소드가 하는 일 설명 [Rename Method] // 배열값의 각 요소들이 이에 대응되는 예측 값과 허용범위 내의 차이를 갖는지 체크한다. public boolean compare(int[] expected, int[] actual, int clipLimit, int delta) { // clipLimit보다 큰 값을 잘라낸다. 블록이 하는 일 설명 [Extract Method] for (int i = 0; i < actual.length; i++) if (actual[i] > clipLimit) actual[i] = clipLimit; // 비교하려는 두 배열값의 길이가 같은지 체크한다. 블록이 하는 일 설명 [Extract Method] if (actual.length != expected.length) return false; // 배열의 각 값의 차이가 delta를 벗어나는지 체크한다. for (int i = 0; i < actual.length; i++) if (Math.abs(expected[i] - actual[i]) > delta) return false; return true; }} 27 Confidential 12/13/2011
  28. 28. Refactoring 기법 : Extract Method 그룹으로 함께 묶을 수 있는 코드조각이 있으면 → 코드의 목적이 잘 드러나는 이름을 갖는 별도의 메소드로 뽑아낸다.{ { // clipLimit보다 큰 값을 잘라낸다. // clipLimit보다 큰 값을 잘라낸다. for (int i = 0; i < actual.length; i++) for (int i = 0; i < actual.length; i++) if (actual[i] > clipLimit) if (actual[i] > clipLimit) actual[i] = clipLimit; actual[i] = clipLimit; }} private void cutLargeValues(int[] values, int limit) { for (int i = 0; i < values.length; i++) if (valuesl[i] > limit) values [i] = limit; } 짧고 이해하기 쉬운 이름으로 된 메소드의 이점  재사용될 확률이 높아짐  메소드를 사용하는 메소드쪽에서 봤을 때 일련의 주석을 읽는 것 과 같은 느낌을 준다  이해하기 쉽다  오버라이드 하기가 쉽다 28 Confidential 12/13/2011
  29. 29. Refactoring 기법: Rename Method 메소드의 이름이 그 목적을 드러내지 못하고 있다면 → 목적에 맞게 메소드의 이름을 바꿔라 좋은 이름은 모듈화된 코드가 빛을 발하기 위한 전제조건 이름을 잘 짓기 위해서  추상화 능력을 기르자  국어를 잘해야 한다. 이름을 잘 짓는 기술을 향상시키는 것이 진정으로 숙련된 프로그래머가 되기 위한 열쇠 중 하나!! 29 Confidential 12/13/2011
  30. 30. Refactoring 기법 : Introduce Assertion 코드의 한 부분이 프로그램의 상태에 대하여 어떤 것을 가정하고 있으면 → assertion을 써서 가정을 명시적으로 만들어라double getExpenseLimit() { double getExpenseLimit() { // should have either expense limit or a primary project return Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null); (_expenseLimit != NULL_EXPENSE) ? return (_expenseLimit != NULL_EXPENSE) ? _expenseLimit: _expenseLimit: _primaryProject.getMemberExpenseLimit(); _primaryProject.getMemberExpenseLimit();} }  Assertion은 프로그램의 동작을 변경시키지 말아야 한다.  Assertion은 항상 참이라고 가정되는 조건문  Assertion이 실패하면 프로그래머가 실수했음을 뜻한다.  Assertion 남용에 주의하자  관리하기에 어려운 논리가 중복될 수 있다  Assertion 내의 조건문을 Extract Method로 명확히 할 수도 있 음 30 Confidential 12/13/2011
  31. 31. 측정할 수 있는 냄새: 긴 메소드 - 개요 항목 설명 징후 • 메소드의 길이가 한 화면을 넘긴다. 원인 • 코딩을 적절한 시점에 끊지 않고, 하나씩 더 추가함 해야 할 일 • 메소드를 나누기 전에 다음 리팩토링 가능성 검토 -> 일직선으로 긴 코드, 조건문, 변수 사용법등 정리? • 의미론 적으로 중요한 코드 블록을 선택 -> ExtractMethod 효과 • 새로운 클래스와 추상화 들어남 • 의사소통 증진 • 중복 노출 금기 긴 메소드가 가장 좋은 표현 방법일 경우 리팩토링 금지
  32. 32. 측정할 수 있는 냄새: 거대한 클래스 항목 설명 징후 • 많은 수의 인스턴스 변수 • 많은 수의 메소드 • 많은 수의 줄 원인 해야 할 일 • 책임의 일부를 분리해 낼 수 있다면 -> Extract Class • 새로운 하위 클래스형태로 뽑아낼 수 있다면 -> Extract Subclass • 기능의 일부를 따로 묶을 수 있다면 -> Extract Interface 효과 • 새로운 클래스와 추상화 들어남 • 의사소통 증진 • 중복 노출 금기
  33. 33. Refactoring 기법: Extract Class 두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우 → 새로운 클래스를 만들고 관련 있는 필드와 메소드를 새로운 클래스로 옮겨라 같이 변하거나, 서로 의존적인 데이터의 부분집합이 있다면,  해당 데이터의 부분집합을 별도의 클래스로 분리! 33 Confidential 12/13/2011
  34. 34. Refactoring 기법: Extract Class – 후보 정하기34 Confidential 12/13/2011
  35. 35. Refactoring 기법: Extract Subclass 어떤 클래스가 일부 인스턴스에 의해서만 사용되는 기능을 가지고 있다면 → 기능의 부분집합을 담당하는 서브클래스를 만들어라 JobItem +getTotalPrice() JobItem +getUnitPrice() +getTotalPrice() +getUnitPrice() +getEmployee() LaborItem +getUnitPrice() +getEmployee() Extract Class와 Extract Subclass 의 선택  Extract Subclass는 한가지 관점의 다양성(variation)만을 수용할 수 있다.  사람에 따라 달라지는 가격대만 처리 가능하다.  Extract Class는 여러가지 관점의 다양성(variation)을 수용 가능하다.  시간대, 지역에 따라 달라지는 가격대를 처리할 수 있다. 35 Confidential 12/13/2011
  36. 36. Refactoring 기법: Extract Interface 여러 클라이언트가 한 클래스 인터페이스의 동일한 부분집합을 사용하고 있거나, 두 클래스가 공통된 인터페이스를 가지는 부분이 있다면 → 그 부분집합을 인터페이스로 뽑아라 <<interface>> Billable +getRate() Employee +hasSpecialSkill() +getRate() +hasSpecialSkill() +getName() Employee +getDepartment() +getRate() +hasSpecialSkill() +getName() +getDepartment() 한 그룹의 클라이언트가 단지 어떤 클래스의 특정 부분집 합만을 사용하는 경우 책임이 어떻게 나뉘어 지는지 명확해짐 Extract Superclass와의 차이점  Extract Interface는 공통된 코드가 아니라 공통된 인터페이스가 추출된다. 36 Confidential 12/13/2011
  37. 37. 측정할 수 있는 냄새: 긴 매개변수 리스트 - 개요 항목 설명 징후 매소드에 매개변수가 한 두 개 보다 많다 원인 • 객체간의 Coupling을 줄이려는 시도에 의해 주로 나타남. 해야 할 일 • 이미 알고 있는 다른 객체로부터 해당 매개변수 값을 얻어올 수 있다면 -> Replace Parameter with Method • 해당 매개변수들이 하나의 객체로부터 넘어온다면 -> Preserve Whole Object 매개변수 데이터가 두 개 이상의 논리적 객체로 부터 얻어와야 한다면 -> Introduce Parameter Object 효과 • 의사소통 증진 • 중복 노출 • 크기 감소 금기 두 클래스 간의 Coupling을 줄일 필요가 있을 때 매개변수 리스트들이 의미있는 그룹으로 묶이지 않을 떄
  38. 38. Refactoring 기법 : Replace Parameter with Method 객체가 메소드를 호출한 다음, 결과를 다른 메소드에 대한 파라미터로 넘기고 있다. 수신자 또한 이 메소드를 호출할 수 있다면 , → 그 파라미터를 제거하고 수신자가 그 메소드를 호출하도록 하라Int basePrice = quantity * itemPrice; int basePrice = quantity * itemPrice;discountLevel = getDiscountLevel(); double finalPrice = getDiscountedPrice(basePrice);double finalPrice = getDiscountedPrice(basePrice, discountLevel);
  39. 39. Refactoring 기법 : Preserve Whole Object 어떤 객체에서 여러 개의 값을 얻은 다음 메소드를 호출하면서 파라미터로 넘기고 있다면 → 대신 그 객체를 파라미터로 넘겨라Int low = daysTempRange().getLow();Int high = daysTempRange().getHigh(); withinPlan = plan.withinRange(daysTempRange());withinPlan = plain.withinRange(low, high)  한 객체에서 얻은 여러 개의 데이터 값을 파라미터로 넘겨줄 때 발생  객체에 필드가 추가될 경우 호출되는 모든 곳을 찾아서 고쳐야 하는 어려움 발생  또한, 호출된 메소드 내에서는 객체가 제공하는 메소드를 접근할 수 없기 때문에 중복코드 발생을 조장함  하지만, 새로운 종속관계가 생기게 됨  종속성 관점으로 분석해야 함
  40. 40. Refactoring 기법: Introduce Parameter Object 자연스럽게 몰려다니는 파라미터 그룹을 가지고 있다면 → 그것들을 객체로 바꾸어라 같이 넘겨지는 경향이 있는 특별한 그룹의 파라미터에 대해  -> 데이터 덩어리  -> 객체형태로 묶일 수 있음 부가 효용  새로 생긴 객체에 들어가야 할 메소드가 쉽게 눈에 띔  이에 따라, 중복을 효과적으로 제거할 수 있음
  41. 41. 이름41 Confidential 12/13/2011
  42. 42. 이름: 개요 좋은 이름의 기능  도메인에 대한 논의의 근간 어휘 제공  의도 명확화  시스템이 어떻게 동작할지에 대한 미묘한 부분 설명  체계적인 이름은 서로 상승작용 일으킴 좋은 이름을 위한 가이드 라인  의사소통을 가장 중요한 가치로 삼는다.  조작 메소드 : 동사형, 접근 메소드 : 명사, 형용사  비슷한 것들에는 같은 단어 사용, 서로 다른것 들에는 다른 단어 사용  한 단어로 된 이름 선호 다루는 냄새  타입이 내장되어 있는 이름(Type Embedded in Name)  의사소통을 방해하는 이름(Uncommunicative Name)  일관성 없는 이름(Inconsistent Name)
  43. 43. 이름: 타입이 내장되어 있는 이름 항목 설명 징후 이름이 단어와 인수의 타입으로 구성되어 있는 복합어 ex) addCourse(Course c) 객체의 타입이 인코딩된 이름이 헝가리안 표기법 ex) iCount 변수 이름이 타입을 나타낸다 원인 • 이름에 타입을 추가하는 것이 더 의사소통에 좋은 효과가 있다고 생각하기 때문 -> 하지만, 이름과 인수의 타입이 중복되는 문제가 발생 • 객체지향 언어에서의 헝가리안 표기법 사용 -> 객체지향 언어에서는 타입이 불필요함 해야 할 일 • 부적절한 이름에 대해서 -> Rename Method, Rename Field, Rename Constant 효과 • 의사소통 증진 • 중복 찾기 쉬워짐 금기 • 팀 내부에 해당 코딩표준이 자리 잡혀 있을 경우, 가독성에 우선순위를 둔다.
  44. 44. 이름: 의사소통을 방해하는 이름 항목 설명 징후 • 지나치게 짧은 이름 • 모음이 생략된 이름 • 번호로 구별되는 변수 • 이상한 약어 • 오해하게 만드는 이름 원인 해야 할 일 • Rename Method, Rename Field, Rename Constant 효과 • 의사소통 증진 금기 * 범위가 충분히 작은 곳에서는 짧은 이름이나, 번호로 구별되는 변수가 의도를 더 잘 전달할 수 있다.
  45. 45. 이름: 일관성 없는 이름 항목 설명 징후 • 같은 것에 대해 여기저기서 서로 다른 이름을 사용한다. Ex) add(), store(), put(), place(), register() 등 원인 • 서로 다른 개발자들이 서로 다른 시기에 클래스를 작성한다. 해야 할 일 • 가장 좋은 이름을 골라 해당 이름으로 나머지 이름들을 수정 -> Rename Method, Rename Field, Rename Constant 효과 • 의사소통 증진 • 중복 노출 금기 • 범위가 충분히 작은 곳에서는 짧은 이름이나, 번호로 구별되는 변수가 의도를 더 잘 전 달할 수 있다.
  46. 46. 좋은 이름을 짓기 위한 규칙 의도를 분명히 밝혀라 그릇된 정보를 피하라 의미 있게 구분하라 발음하기 쉬운 이름을 사용하라 검색하기 쉬운 이름을 사용하라 인코딩을 피하라 자신의 기억력을 자랑하지 마라46 Confidential 12/13/2011
  47. 47. 불필요한 복잡성47 Confidential 12/13/2011
  48. 48. 불필요한 복잡성: 죽은 코드 & 추측 성 일반화 항목 죽은 코드 추측 성 일반화 징후 • 변수, 매개변수, 필드, 코드의 일부분, 메서드 • 사용되지 않는 클래스, 메서드, 필드 매개변수 등 혹은 클래스가 테스트를 제외한 어느곳에서 이 있다. 도 사용되지 않는다. • 코드가 현재 구현된 요구사항에 대해 필요 이상으 로 복잡하다 원인 • 요구사항이 변했다. • 일반성이나 완결성을 위해 지나치게 많은 코드를 • 새로운 접근방식 도입 후 이전코드 정리하지 추가함으로써 발생 않음. • 복잡한 로직코드에 의해 실제로 실행되지 않 는 코드 발생해야 할 일 • 사용하지 않는 코드 및 테스트 코드 삭제 • 기능을 부모나 자식 클래스로 옮길 수 있는 클래스 -> Collapse Hierarchy • 기능을 호출자로 옮길 수 있는 클래스 -> Inline Class • 필요없는 메서드 -> Inline Method • 필요없는 필드 -> 제거 • 필요없는 매개변수 -> Remove Parameter 효과 • 코드 크기 간소화, 의사소통 증진, 단순성 향상 금기 • 프레임워크 등을 개발한다면, 죽은 코드로 보이는 존재할 것이지만 실제로 죽은 코드는 아니다.48 Confidential 12/13/2011
  49. 49. Refactoring 기법: Inline Class & Collapse Hierarchy* Inline Class 클래스가 별다른 일을 하지 않는다면 → 해당 클래스의 기능을 다른 클래스로 옮기고 삭제한다.* Collapse Hierarchy 수퍼클래스와 서브클래스가 별로 다르지 않다면 → 그것들을 하나로 합쳐라 LoginContext LoginModule +authenticate() Collapse Hierarchy IDPasswordLoginModule KerberosLoginModule x x FingerPrintLoginModule +authenticate() +authenticate() +authenticate() 49 Confidential 12/13/2011
  50. 50. Refactoring 기법 : Inline Method 메소드의 내용이 그 이름만큼 명확할때 → 메소드의 내용을 호출자로 옮기고 메소드를 삭제한다.int getRating() { int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; return (_numberOfLateDeliveries > 5) ? 2 : 1;} }boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5;}  과도한 Indirection 사용 시 적용  메소드의 내용이 이름만큼이나 명확할 때  잘못 Refactoring된 코드  메소드들의 내용을 하나의 큰 메소드에 포함한 다음 다시 Refactoring 50 Confidential 12/13/2011
  51. 51. 중복51 Confidential 12/13/2011
  52. 52. 중복: 개요 중복은 재난의 구렁텅이 중복이 야기시키는 문제  관리해야 할 코드가 더 많아진다.  변경될 수 있는 부분이 변경되지 않는 부분 속에 파묻혀 있다.  조금씩 다른 코드로 인해 유사성 파악이 힘들게 된다.  기능 추가 및 버그 수정이 누락될 여지가 크다. 다루는 냄새  매직넘버(Magic Number)  중복된 코드(Duplicated Code)  다른 인터페이스를 갖는 대체 클래스(Alternative Classes with Different Interfaces)
  53. 53. 중복: 매직넘버 항목 설명 징후 • 코드 중간에 상수가 나타난다. 원인 • 누군가 필요에 의해 상수를 코드에 삽입 해야 할 일 • Replace Magic Number with Symbolic Constant • 값이 문자열일 경우 국제화를 위해 매핑 도구 활용 효과 • 중복 축소 • 의사소통 증진 금기 • 테스트코드에서는 필요한 값을 바로 사용할 때가 더 읽기 편한 경우 있음 • 하지만, 상수가 다른 값으로 부터 유도된 값이라면 기호상수를 이용하여 빼낼 필요 있음
  54. 54. 중복 : 매직넘버public void analyze(File file) throws FileNotFoundException { private static final int INDEX_TYPE = 0; BufferedReader reader = new BufferedReader(new FileReader(file)); private static final int INDEX_TIME = 1; private static final int INDEX_URL = 2; try { private static final int INDEX_IPADRESS = 3; String aLine = reader.readLine(); public void analyze(File file) throws FileNotFoundException { while (aLine != null) { BufferedReader reader = new BufferedReader(new FileReader(file)); // type, time, id, ip address String[] record = aLine.split(SEPERATOR); try { String aLine = reader.readLine(); AccessLog aLog = new AccessLog(); aLog.setType(record[0]); while (aLine != null) { aLog.setTime(record[1]); // type, time, id, ip address aLog.setUrl(record[2]); String[] record = aLine.split(SEPERATOR); aLog.setIpAddress(record[3]); Replace Magic Number with Symbolic Constant AccessLog aLog = new AccessLog(); aLine = reader.readLine(); aLog.setType(record[INDEX_TYPE]); } aLog.setTime(record[INDEX_TIME]); } catch (IOException e) { aLog.setUrl(record[INDEX_URL]); e.printStackTrace(); aLog.setIpAddress(record[INDEX_IPADRESS]); } finally { // .. aLine = reader.readLine(); } }} } catch (IOException e) { e.printStackTrace(); } finally { // .. } }→ 중복축소, 의사소통 증진 54 Confidential 12/13/2011
  55. 55. 중복: 중복된 코드 항목 설명 징후 • 두 코드가 거의 동일해 보인다. • 두 코드의 기능이 거의 동일하다. 원인 • 개발자들이 서로 다른부분에서 독립적으로 작업함으로 인해 발생 • 중복을 제거할 정도로 부지런하지 못함 • 다른 냄새가 중복을 가리고 있는 경우 • 의도적인 Copy & Paste 해야 할 일 • 하나의 메서드 또는 클래스에서 중복 -> Extract Method • 두 형제클래스에 중복 -> Extract Method -> Pull Up Field or Pull Up Method -> Form Template Method • 서로 관련 없는 두 클래스에 중복 -> Extract Class • 물리적으론 동일하지 않지만, 같은 기능 수행 -> Substitute Algorithm 효과 • 중복 줄임 • 더 나은 추상화 및 더 유연한 코드를 얻을 수 있다. 금기
  56. 56. 중복: 중복된 코드 실습56 Confidential 12/13/2011
  57. 57. 중복: 다른 인터페이스를 갖는 대체 클래스 항목 설명 징후 • 같은 일을 하는 두 클래스에 사용된 메서드 이름이 서로 다름 원인 • 프로그래머가 비슷한 상황을 다루기 위해 비슷한 일을 하지만, 다른 코드가 존재한다는 것을 인식하지 못함 해야 할 일 • 클래스 중 하나를 제거할 수 있도록 클래스들 조율 -> Rename Method 이용 메서드 이름 유사하게 만듬 -> Move Method, Add Parameter, Parameterize Method 이용 프로토콜 유사하게 만듬 -> Extract Superclass -> 나머지 클래스 삭제 효과 • 중복 축소 금기 • 두 클래스가 각각 별개의 라이브러리에 속해 있을 경우.
  58. 58. 조건로직58 Confidential 12/13/2011
  59. 59. 조건로직: 개요 쉽게 발생될 여지가 큼 조건문이 복잡해 지면?  다양한 경로를 고려해야 함  빠지는 조건이 발생할 여지가 큼 조건 문을 만들다 보면 특수 조건문을 만들기 쉬움 조건로직은 객체지향적 접근방법을 충분히 고려하지 못해 사용되는 경향이 있음 다루는 냄새  복잡한 Boolean 표현식(Complicated Boolean Expression)  가장된 상속(Simulated Inheritance) 59 Confidential 12/13/2011
  60. 60. 조건로직: 복잡한 Boolean 표현식점수, 급여, 공인 여부에 따라 대출 승인 여부를 리턴원본코드) public boolean isAcceptable1(int score, int income, boolean authorized) { if (!((score > 700) || ((income >= 40000) && (income <= 100000) && authorized && (score > 500)) || (income > 100000)) ) return false; else return true; } DeMorgan’s Law수정코드)public boolean isAcceptable2(int score, int income, boolean authorized) { if (((score <= 700) && ((income < 40000) || (income > 100000) || !authorized || (score <= 500)) && (income <= 100000)) ) return false; else return true; } 60 Confidential 12/13/2011
  61. 61. Refactoring 기법: Introduce Explaining Variable점수, 급여, 공인 여부에 따라 대출 승인 여부를 리턴원본코드) public boolean isAcceptable1(int score, int income, boolean authorized) { if (!((score > 700) || ((income >= 40000) && (income <= 100000) && authorized && (score > 500)) || (income > 100000)) ) return false; else return true; } Introduce Explaining Variable수정코드) public boolean isAcceptable3(int score, int income, boolean authorized) { boolean hasMidRangeIncome = (income >= 40000) && (income <= 100000); boolean hasHighIncome = income > 100000;; boolean hasHighScore = score > 700; if (!( hasHighScore || (hasMidRangeIncome && authorized && (score > 500)) || hasHighIncome) ) return false; else return true; } 61 Confidential 12/13/2011
  62. 62. Refactoring 기법 : Introduce Explaining Variable & DeMorgan’s LawIntroduce Explaining Variable) public boolean isAcceptable3(int score, int income, boolean authorized) { boolean hasMidRangeIncome = (income >= 40000) && (income <= 100000); boolean hasHighIncome = income > 100000;; boolean hasHighScore = score > 700; if (!( hasHighScore || (hasMidRangeIncome && authorized && (score > 500)) || hasHighIncome) ) return false; else return true; } Introduce Explaining Variable수정코드) & DeMorgan’s Law public boolean isAcceptable4(int score, int income, boolean authorized) { boolean hasMidRangeIncome = (income >= 40000) && (income <= 100000); boolean hasHighIncome = income > 100000;; boolean hasHighScore = score > 700; if (!hasHighScore && (!hasMidRangeIncome || !authorized || !(score > 500)) && !hasHighIncome) return false; else return true; } 62 Confidential 12/13/2011
  63. 63. Refactoring 기법: Decompose Conditional원본코드) public boolean isAcceptable1(int score, int income, boolean authorized) { if (!((score > 700) || ((income >= 40000) && (income <= 100000) && authorized && (score > 500)) || (income > 100000)) ) return false; else return true; } Decompose Conditional수정코드) public boolean isAcceptable5(int score, int income, boolean authorized) { if (!(isHighScore(score) || ( isMidRangeIncome(income) && authorized && isMidScore(score)) || isHighIncome(income)) ) return false; else return true; } 63 Confidential 12/13/2011
  64. 64. Refactoring 기법: 조건문 치환Decompose Conditional) public boolean isAcceptable5(int score, int income, boolean authorized) { if (!(isHighScore(score) || ( isMidRangeIncome(income) && authorized && isMidScore(score)) || isHighIncome(income)) ) return false; else return true; } 조건문 치환조건문 치환) public boolean isAcceptable6(int score, int income, boolean authorized) { if (isHighScore(score) || ( isMidRangeIncome(income) && authorized && isMidScore(score)) || isHighIncome(income) ) return true; else return false; } 64 Confidential 12/13/2011
  65. 65. Refactoring 기법: Consolidate Conditional Expression조건문 치환) public boolean isAcceptable1(int score, int income, boolean authorized) { if (!((score > 700) public|| ((income >= 40000) && (income <= 100000) &&boolean authorized) boolean isAcceptable6(int score, int income, authorized && (score > 500)) { || (income > 100000)) ) ifreturn false; (isHighScore(score) else|| ( isMidRangeIncome(income) && authorized && isMidScore(score)) || isHighIncome(income) return true; } ) return true; else return false; } Consolidate Conditional ExpressionConsolidate Conditional Expression) public boolean isAcceptable8(int score, int income, boolean authorized) { if (isHighScore(score) || isHighIncome(income)) // return true; if (isMidRangeIncome(income) && authorized && isMidScore(score)) return true; else Refactoring을 함으로써 return false; } - 조건의 재사용으로 중복 축소 - 조건로직 명확화로 의사소통 증진 - 성능 향상의 포인트를 찾기 쉬워짐 65 Confidential 12/13/2011
  66. 66. 복잡한 Boolean 표현식 실습66 Confidential 12/13/2011
  67. 67. 조건로직 : 가장된 상속문 해야 할 일public class Employee { private int type; private static final int ENGINEER = 0;  1단계 private static final int SALESMAN = 1; private static final int MANAGER = 2;  public Employee(int type) { this.type = type;  객체가 생성된 이후 Type } 이 변경될 수 있는 경우 Replace Type Code with public int calculatePayment() { switch (type) {  case ENGINEER: return monthlySalary; State/Strategy 변경되지 않는 경우 case SALESMAN: return monthlySalary + commission;  case MANAGER: return monthlySalary + bonus;  Replace Type Code with default: throw new RuntimeException("Incorrect Employee"); Subclass }} public String getJobTitle() {  2단계 switch (type) { case ENGINEER:  Replace Conditional with return "Engineer"; case SALESMAN: Class return "Salesman"; case MANAGER: return "Manager"; default: throw new RuntimeException("Incorrect Employee"); } }} 67 Confidential 12/13/2011
  68. 68. 복잡한 Boolean 표현식 실습68 Confidential 12/13/2011
  69. 69. 클래스 사이의 냄새69 Confidential 12/13/2011
  70. 70. 데이터70 Confidential 12/13/2011
  71. 71. 데이터: 개요 배송 시스템의 Book 클래스 쇼핑몰 시스템의 Book 클래스 height title genre category width author weight price getDiscount() getRequiredDeliveryRoom() content Book 데이터는 의사소통의 기점이 됨 누락된 클래스, 잘 못 만들어진 클래스의 징후 데이터가 잘 조직화 되어 있으면, 그 안에 속해있는 행위를 발견할 수 있음71 Confidential 12/13/2011
  72. 72. 데이터: 기본 타입에 대한 강박관념 징후  기본타입 또는 기본 타입에 가까운 타입의 사용  작은 정수값을 나타내는 열거형 및 상수  필드명을 나타내는 문자열 상수 원인  누락된 클래스 ->Replace Data Value with Object  가장된 타입 -> 조건으로 사용되지 않는다면: Replace Type Code with Class  가장된 필드접근자 : Replace Array with Object 72 Confidential 12/13/2011
  73. 73. Refactoring 기법 : Replace Data with Object User +firstName: String +lastName: String Order Customer Replace Data with+orderNumber: String Object Order +firstName: String+customeFirstNamer: String 1 1 +lastName: String+customerLastName: String +orderNumber+price: float+addressState: String Address+addressCity: String +state+addressStreet: String +city+addressZipCode1: String +zipCode1+addressZipCode 2: String 1 1 +zipCode2+telAreaCode: String+telNumber: String +getDeliveryCost() home Contact office +areaCode +number 73 Confidential 12/13/2011
  74. 74. Refactoring 기법 : Replace Array with Object 배열의 요소들이 서로 다른 뜻을 가지고 있다면 → 배열을 각각의 요소에 대한 필드를 가지는 객체로 바꿔라String[] row = new String[3]; Performance row = new Performance(); row [0] = "Liverpool"; row.setName("Liverpool"); row [1] = "15"; row.setWins("15"); 배열은 동일한 의미를 갖는 비슷한 객체가 순서대로 모여 있는 경우에 대해서만 사용되어야 함 74 Confidential 12/13/2011
  75. 75. 데이터: 데이터 클래스 public field 또는 단순 getter/setter로 이루어져 있음 원인  데이터 덩어리를 클래스로 추출하는 과정에서 생김 해야 할 일  1단계-기본 필드 캡슐화 : Encapsulate Field  2단계-불필요한 세팅메소드 제거 : Remove Setting Method  3단계-Collection 필드 캡슐화 : Encapsulate Collection  4단계-클라이언트 코드에서 관련 로직 이동 : Extract Method and Move Method  5단계-중복정리 주의점  자주 접근되는 필드의 경우, 성능상 문제 이유 때문에, public 필드로 남겨 질 경우 있음  리플렉션 또는, getter/setter 메서드에 의존 75 Confidential 12/13/2011
  76. 76. Refactoring 기법: Encapsulate Collection Collection을 get/set하는 메소드가 있으면 → 그 메소드가 읽기전용 뷰(read-only view)를 리턴하도록 만들고, add/remove 메소드를 제공하라 위험  Collection을 리턴하는 메소드(get)  클라이언트 코드가 리턴받은 Collection의 데이터를 조작할 수 있 다.  Collection을 파라미터로 받는 메소드(set)  Collection을 넘겨준 클라이언트 코드가 Collection의 데이터를 조작할 수 있다. 76 Confidential 12/13/2011
  77. 77. Encapsulate Collection 실습77 Confidential 12/13/2011
  78. 78. 상속78 Confidential 12/13/2011
  79. 79. 상속: 거부된 유산 항목 설명 징후 • 하위클래스가 부모클래스로부터 상속한 메소드에서 예외를 던진다.(명시적 거부) • 하위클래스가 부모클래스로부터 상속한 메소드에서 아무일도 안한다.(암시적 거부) • 클라이언트가 하위클래스를 직접 사용하는 경향이 있다. • 상속구조가 적절하지 않으며, 하위 클래스가 부모 클래스와 Is-A 관계가 아니다 원인 • 구현상의 편의를 위해서만 상속을 사용 해야 할 일 • 혼동되지 않으면 그대로 놔둘 수 있음 • 상속관계 유지할 필요 없으면, Replace Inheritance with Delegation 적용 • 상속구조 재 조정 Extract Subclass, Push Down Field, Push Down Method 효과 금기 • 때때로, 거부된 유산은 새로운 타입의 폭주를 막기 위해 의도적으로 사용될 수도 있다. -> Java 의 읽기전용 Collection79 Confidential 12/13/2011
  80. 80. 상속: 거부된 유산 Drawable Drawable +draw() Line 추가 +draw() Exception +getArea() +getArea() 발생시키거나, 0을 리턴Rectangle Circle Rectangle Circle Line +getArea() 상속구조 재조정 Drawable +draw() Shape Line +getArea() +getLength() Rectangle Circle80 Confidential 12/13/2011
  81. 81. Liskov Substitution Principle(LSP)“Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.” 고유 특성을 고려하면, Square는 Rectangle의 일부임으로 문제가 없어 보인다. -> 하지만, 행위를 고려하면 LSP에 위배된다. Shape Rectangle.setWidth() 와 Square.setWidth()가 하는 일은 다를 것이다. +getArea() Shape Rectangle Circle +getArea() 상속구조 재조정+width+height+setWidth()+setHeight()+getWidth() Square Rectangle Circle+getHeight() +width +width +height +height +setLength() +setWidth() +setHeight() Square +getWidth() +getHeight()“OOD에서 IS-A관계는 외적으로 보여지는 행위에 기반하여 도출하여야 한다.”81 Confidential 12/13/2011
  82. 82. Refactoring 기법: Replace Inheritance with Delegation서브클래스가 수퍼클래스 인터페이스의 일부분만 사용하거나 또는 데이터를상속받고 싶지 않은 경우→ 수퍼클래스를 위한 필드를 만들고 메소드들이 수퍼클래스에 위임하도록 변경한 후 상속 관계를 제거82 Confidential 12/13/2011
  83. 83. 상속: 부적절한 친밀(하위 클래스 형태) 항목 설명 징후 • 어떤 클래스에서 부모 클래스의 내적인 부분(private이어야만 하는)에 접근한다. 원인 • 부모클래스와 자식 클래스가 필요이상으로 강하게 결합되어 있다. 해야 할 일 • 하위 클래스가 제어되지 않는 방식으로 부모클래스의 필드에 접근 -> Self Encapsulate Field • 부모클래스가 자식 클래스들이 연결할 수 있는 일반 알고리즘 정의 가능 -> Form Template Method • 결합도를 더욱 줄일 필요 -> Replace Inheritance with Delegation 효과 금기83 Confidential 12/13/2011
  84. 84. Refactoring 기법: Form Template Method각각의 서브클래스에, 동일한 순서로 비슷한 단계를 행하지만, 단계가 완전히같지는 않은 두 메소드가 있다면,→ 그 단계를 동일한 시그너처를 가진 메소드로 만들어라, 이렇게 하면 원래의 두 메소드는 서로 같아지므로, 수퍼클래스로 올릴 수 있다.84 Confidential 12/13/2011
  85. 85. 상속: 게으른 클래스 항목 설명 징후 • 클래스가 많은 일을 하지 않고, 대부분 Delegation한다. 원인 • 부모클래스와 자식 클래스가 필요이상으로 강하게 결합되어 있다. 해야 할 일 • 부모클래스로 옮길 수 있다면 -> Collapse Hierarchy • 그 이외 -> Inline Class 효과 금기 • 때로는 의도를 나타내기 위해서 게으른 클래스를 사용하기도 한다.85 Confidential 12/13/2011
  86. 86. 책임86 Confidential 12/13/2011
  87. 87. 책임: 개요 다루는 냄새  기능에 대한 욕심  부적절한 친밀  메시지 체인  미들맨 87 Confidential 12/13/2011
  88. 88. 책임: 기능에 대한 욕심 & 부적절한 친밀 Class A Class B Class A Class B .. .. .. attributeA amethod() getX() amethod() bmethod() getY() bmethod() amethod() 기능에 대한 욕심 부적절한 친밀 특정 클래스 내의 메소드가 동작을 위해 다른 클 특정 클래스에서 다른 클래스의 private 멤버를 접근징후 래스에 있는 정보를 많이 필요로 하는 경우 한다. 두 개의 독립적인 클래스가 엉켜 있다면 - Move Method 중재하는 클래스가 없기 때문에 엉켜있다면해결책 Move Method - Extract Hierarchy-> Hide Delegate 클래스들이 서로 가르키고 있다면 - Change Bidirectional Reference to Unidirectional88 Confidential 12/13/2011
  89. 89. Refactoring 기법: Hide Delegate 어떤 클래스가 다른 클래스의 위임 클래스를 직접 호출하고 있는 경우, → 서버에 메소드를 만들어서 대리객체 (Delegate)를 숨겨라 직교성을 높여준다.  Client 클래스가 더 이상 Department의 변화에 대한 종속성을 갖 지 않는다. Law of Demeter를 따른다. 부작용 : 게으른 클래스가 될 수 있다. 89 Confidential 12/13/2011
  90. 90. Refactoring 기법: Change Bidirectional Association to Unidirectional 서로 링크를 가지는 두 개의 클래스에서 한쪽이 다른쪽을 더 이상 필요로 하지 않을때 → 불필요한 링크를 제거하라. Bidirectional Association은 사용하는 측면에선 편하다. 하지만 다음과 같은 위험을 지님  복잡성 증가 -> 버그발생의 원천  Zombie 객체 생성 실수  자연적으로 Cluster결성 -> Coupling 증가 90 Confidential 12/13/2011
  91. 91. 책임 : 메시지 체인 & 미들맨 Remove Middleman Hide Delegate 메시지 체인 미들맨 다음과 같은 패턴이 나타난다. 어떤 클래스에 있는 대부분의 메서드에서 다른 객체 징후 person.getDepartment().getManager() 에 있는 동일한 메서드나 유사한 메서드를 호출한다  Delegate 객체에 새로운 Feature가 생겨도  객체들간의 커플링이 감소한다. 장점 Server객체의 변경이 필요없다.  생성해야 할 테스트 케이스가 좀 더 적다.  객체들간의 커플링이 증가한다.  Delegate 객체에 새로운 Feature가 생기면 Server 단점  테스트해야 할 케이스가 좀 더 많다. 객체에도 변경이 필요하다. Hide Delegate를 이용하여 객체를 하나의 객체와 Remove Middleman 을 통하여 클라이언트가 직접해결책 종속적이게 만듬 호출하게 함☞ 정답은 없다! 코드의 진화에 따라, 상황에 적합한 방법을 사용하도록 하자. 91 Confidential 12/13/2011
  92. 92. 변경 수용하기92 Confidential 12/13/2011
  93. 93. 변경 수용하기: 개요 다루는 냄새  여러 원인에 의한 변경(Divergent Change)  평행상속계층구조(Parallel Inheritance Hierarchies)  조합적 폭발(Combinational Explosion)  산탄총 수술(Shotgun Surgery) 93 Confidential 12/13/2011
  94. 94. 변경 수용하기: 여러 원인에 의한 변경(Divergent Change) Shape Shape ShapWriter +draw() 기본 선 두께를 +draw() +persist() +persist() ‘5px’로 했으면.. Binary형태 뿐 아니라, XML형태로도 저장이 되었으면.. Rectangle Circle Triangle RectangleWriter CircleWriter TriangleWriter Rectangle Circle Triangle +draw() +draw() +draw() +persistence() +persistence() +persistence() +draw() V +draw() V +draw()V +persist() V +persist() V +persist() V 징후  하나의 클래스를 서로 다른 이유로 변경한다. 원인  하나의 클래스가 두 가지 이상의 책임을 갖는다. 해야 할 일  Extract Class를 통해 책임을 별도의 Class로 뽑아낸다. 94 Confidential 12/13/2011
  95. 95. 변경 수용하기: 여러 원인에 의한 변경 식별법 직관적으로 SRP Test Method를 Uses관계로 그룹화 또는, Used관계로 그룹화 형상관리 History 분석  같이 변경되는 클래스 또는 메서드의 그룹이 상이하다면? 95 Confidential 12/13/2011
  96. 96. 변경 수용하기: 평행상속계층구조 Shape ShapWriter +draw() +persist() Rectangle Circle Triangle RectangleWriter CircleWriter TriangleWriter +draw() +draw() +draw() +persistence() +persistence() +persistence() 징후  어떤 계층구조의 하위클래스 생성 -> 또 다른 계층구조의 하위클래스 생성필요 원인  편의상 동일한 계층구조가 생성됨  대부분 짝을 이루는 클래스는 동일한 결정의 서로 다른 측면을 표현한다. 해야 할 일  Move Method, Move Field를 한쪽 계층구조를 제거 96 Confidential 12/13/2011
  97. 97. 변경 수용하기 : 조합적 폭발 ShapeWriter Triangle +persist() 추가? DB 추가? Rectangle Circle +draw() +draw() RectangleExcelWriter RectangleXmlWriter RectangleExcelWriter RectangleXmlWriter 징후  새로운 하위클래스 추가를 위해서 같은 계층구조의 여기저기 다수의 클래스를 추가해야 함 원인  두 개 이상의 관점(or Concern)이 하나의 계층구조로 모델링 되어 있다. 해야 할 일  Replace Inheritance with Delegation  Tease Apart Inheritance 97 Confidential 12/13/2011
  98. 98. Refactoring 기법: Tease Apart Inheritance 두 가지 작업을 한번에 처리하는 상속 구조가 있는 경우 → 두 개의 상속구조를 만들고, 하나가 다른 하나를 호출하도록 위임하라. 어떤 작업이 더 중요한지를 따져서, 중요한 작업을 남 기고 나머지 작업을 새로운 상속구조로 옮긴다. 98 Confidential 12/13/2011
  99. 99. 변경 수용하기: 산탄총 수술(Shotgun Surgery) 징후  간단한 변경을 위해 여러 클래스를 수정해야 한다. 원인  하나의 책임이 여러 군데 분산되어 있다.  책임 전체를 담당할 클래스가 누락되어 있다. 해야 할 일  책임을 담당할 클래스 생성  Move Method, Move Field를 통해 관련 메소드 & 필드 이동 예  구성정보  로깅정보  Persistence 관련 정보 99 Confidential 12/13/2011
  100. 100. 라이브러리 클래스100 Confidential 12/13/2011
  101. 101. 라이브러리 클래스: 개요 징후  사용하는 라이브러리에 원하는 기능이 없다. 해야 할 일  한 두개의 메서드 추가 필요  Introduce Foreign Method  여러 개의 메서드 필요  Introduce Local Extension  라이브러리를 다루는 레이어 추가 [Introduce Local Extension] 101 Confidential 12/13/2011

×