12. 프로젝트 In IDEAL Project in the world
확정되어 변경되지 않는 고객의 요구 사항
빈틈없는 시스템 설계
숙련되고 표준을 따르기를 즐기는 개발자
하나의 버그도 빠져나갈 수 없는 완벽한 테스트
정확히 산정된 스케줄과 여유 있는 납기 일자
12
13. 프로젝트 In REAL Project in the world
파트리더 고미화의 고민
– 개발자가 모두 동일한 수준의 Skill을 가지고 있지 않다.
– 고객의 요구는 계속 변화된다.
– 개발 초기의 디자인이 완벽하지 않는 경우가 많으며,
– 실제로 설계가 잘못된 경우도 많다.
– 테스트에 나타나지 않는 버그들이 존재한다
– 개발 완료로 끝나지 않는다. 유지보수가 뒤 따른다.
13
14. 프로젝트 In REAL Project in the world
개발자 최고수의 고민
14
15. 프로젝트 In REAL Project in the world
개발자 최고수의 고민
– 코드 이해에 많은 시간이 필요(복잡,다양한 개발자의 취향)
– 어려운 업무 용어가 많이 들어간 장황한 주석 또는 주석 부재
– 수정으로 인한 파급 효과에 대한 불안
– Copy & Paste 로 신규 기능 작성
– 고치면 고칠수록 수정이 어려워짐 (땜빵식 코드)
15
16. Normal Dejavu Project in the world
• (어쨌든) 요구사항에 맞춰 동작하는 코드 완성
• 추가 요구사항을 계속 반영, 반영, 반영
• 많은 부분에 있어 수정이 가해지고, 개발 속도가 느려지기 시작
• 가끔은 수정해야 할 부분을 잊어서 에러도 발생 (Iterative)
• 후임 등장
• 소스를 보고 기겁, 어떻게 어딜 고쳐야 하나?
• 언제 시간 내서 통째로 다시 재 작성해야 한다고 믿음
• 그러나 여전히 부분 부분 수정하며, 필요 시에는 선임자를 전화로 찾음
• (어쨌든) 요구사항에 맞춰 동작하는 코드 완성
• and Looping…
16
19. 리팩터링이란? 리팩터링이란?
리팩터링
– 외견상 보이는 기능 동작은 바뀌지 않는 상태에서
– 이해하기 쉽고,
– 수정하기 쉽도록
– 소프트웨어의 내부적인 구조를 변경하는 것
19
20. Example #1 리팩터링이란?
void f() {
void f() { ….
…. computeScore();
// Compute score }
score = a * b + c;
score -= discount; void computeScore() {
} score = a * b + c;
score -= discount;
}
20
21. Example #2 리팩터링이란?
public String name;
private String name;
public String getName(){
return name;
}
public String setName (String newName) {
name = newName;
}
21
22. Example #3 리팩터링이란?
if (user == null)
plan = Plan.basic();
else
plan = user.getPlan();
plan = user.getPlan();
<<interface>>
User
+getPlan()
NullUser
+getPlan() return Plan.basic();
22
23. 왜 리팩터링을 하는가? 리팩터링이란?
설계를 개선 하기 위해
코드를 이해하기 쉽게 만들기 위해
버그를 찾아내기 위해
좀 더 빠르게 프로그래밍 할 수 있도록
개발자와 관리자에게 어떤 하나의 대안 될 수 있는가?
23
24. 구성요소 리팩터링이란?
나쁜냄새(Smells)
카탈로그(Catalogue)
자동화된 단위 테스트(Automated Unit Test)
24
25. 문제점 리팩터링이란?
데이터 베이스
인터페이스 변경
프로젝트 데드라인
25
26. 언제 리팩터링할 것인가? 리팩터링이란?
The Rule of Three – DRY(Don’t Repeat Yourself)원칙
기능을 추가할 때
버그를 수정 할 때
코드 리뷰를 할 때
26
27. 성능의 문제 리팩터링이란?
소프트웨어를 더 이해하기 쉽게 만들기 위해 수정을 하는데, 이때
종종 프로그램을 더 느리게 작동하도록 하는 원인이 될 수 있다.
잘 분해된 코드와 프로그램이 주는 이점
– 새로운 기능 추가가 쉬워 퍼포먼스 튜닝에만 집중하는 것이 가능하다.
– 세밀한 분석이 가능하다.
27
28. 안전한 리팩터링의 3대 요소 리팩터링이란?
백업을 만들어 놓거나 형상관
리를 사용한다.
검증된 리팩터링 도구를 사용
한다.
자동화된 단위 테스트 케이스
를 만들어 놓는다.
28
31. 단위 테스트 Automated Unit Test 테스트 코드
“리팩터링 작업의 필수 조건은 사전에 탄탄한 테스트를 만들어 놓
는 것이다.” – 마틴 파울러
가장 대표적인 자동화된 단위 테스트 프레임워크
= JUnit
31
32. 테스트 주도 개발 Test-Driven Development 테스트 코드
자동화된 단위 테스트 케이스를 만들어 내는 가장 훌륭한 방식
“프로그램을 작성하기 전에 자동화된 테스트 코드를 먼저 작성한다.”
간결한 설계와 고품질 모듈을 만들어 내는 기법
32
33. TDD 절차 테스트 코드
질문 Ask
테스트를 작성함으로써 시스템에 질문한다. (FAIL)
응답 Respond
테스트를 통과하는 코드를 작성해서 질문에 대답한다. (PASS)
정련 Refine
아이디어를 통합하고, 불필요한 것은 제거하고, 모호한 것은
명확히 해서 대답을 정제한다. (REFACTORING)
반복 Repeat
다음 질문을 통해 대화를 계속 진행한다.
33
35. 소프트웨어 비용 리팩터링과 설계
전체 비용 = 개발 비용 + 유지 비용
유지 비용 = 이해 비용 + 수정 비용 + 테스트 비용
+ 설치 비용
35
36. 사전 설계 Upfront Design 리팩터링과 설계
사전 설계
– 재 작업 비용을 줄이기 위해서 SW구조에 대해 사전에 고민을 하는 방식
– 요구사항 변경이 일어날 부분에 대해서 미리 예측한다.
– 좀 더 유연한 구조의 설계를 지향한다.
유연한 구조로 솔루션을 구축할 때의 문제점
– 다양한 유연성을 가진다는 건 다른 의미의 비용으로 이어진다.
– 좀 더 복잡한 구조가 되기 쉽고, 일반적으로 유지관리하기 더 어려워진다.
– 그리고 미래를 정확히 예측하기는 것은 대부분의 경우 불가능하다.
36
37. 리팩터링과 설계 리팩터링과 설계
리팩터링은 일부 관점이 사전설계(upfront design)와 충돌하지
만 결과적으로 리팩터링은 디자인을 보완한다.
– 리팩터링과 사전 설계는 강조점이 다르다. 완벽한 솔루션을 찾는 것이 아
니라 납득할만한 수준(reasonable)의 솔루션을 찾는다.
– 유연성 강조보다 설계를 간결하게 만드는 쪽에 좀 더 관심을 둔다.
– 간결한 시스템을 좀 더 복잡한 유연한 시스템으로 만드는 것은 매우 쉽지
만, 그 반대는 훨씬 어렵기 때문이다.
37
39. 직관 Intuition Smells
1997년 5월 11일
뉴욕의 에퀴터블 센터에서는 IBM이 개발한 수퍼 컴퓨터 딥 블루(Deep
blue)와 세계 체스 챔피언 ‘게리 카스파로프(Kasparov)’간의 6 차전
마지막 경기가 진행되고 있었다.
『딥 블루』는 2m가 채 안되는 키에 1.4 톤이나 나가는 몸무게를
가지고 있으며 중국인 과학자 츄펑슝 박사 외에 6명의 IBM사 최고
과학자들이 지난 8년 동안 32개의 마이크로 프로세서와 512개의 특수
체스 칩을 장착해서 만들은 2백만 달러 상당의 대형 병렬 수퍼 컴퓨터다.
『딥』은 오로지 『인간을 이겨라』는 특명을 받고 태어난 역사상 최고의
기계로써 1초당 2억번의 행마(行馬)를 검토해가며 장기를 둘 수 있는
능력이 있으며, 지난 백 년 동안에 개최되었던 주요 체스 대국의 기보가
모두 입력되어 있다. 더욱이 슈 박사 팀은 지난번의 패배를 설욕하기 위해
연산 능력을 2배로 보강했으며 미국의 체스 챔피언 조엘 벤저민과 세계
유명 체스 선수들의 조언을 추가로 저장하였다. 특히 슈 박사 팀은
어떠한 경우에도 상대수의 변화에 따라 새로운 수를 개발하여 대처할 수
있는 자기 조정기능의 유연성을 강화시키는데 초점을 두었다고 한다.
따라서『딥』은 그야말로 체스에 있어서는 『살아있는 지능을 가지고
있는 컴퓨터』라고 할 수 있다
지난 5차전까지 이 두 대국자간의 전적은 1승 3무 1패로 무승부.
- New York Times 1997.5
39
40. 코드 속의 나쁜 냄새 #1 Smells
“나쁜 냄새가 난다면, 고쳐라!” – 켄트벡
– 중복코드(Duplicate code)
– 긴 메소드(Long Method)
– 거대한 클래스(Large class)
– 긴 파라미터 리스트(Long Parameter List)
– 확산적 변경(Divergent Change)
– 산탄총 수술(Shotgun Surgery)
– 기능 욕심(Feature Envy)
– 데이터 덩어리(Data Clumps)
– 기본형에 대한 강박(Primitive Obsession)
– 스위치 구문(Switch Statements)
– 병렬 상속 구조(Parallel Inheritance Hierarchies)
40
41. 코드 속의 나쁜 냄새 #2 Smells
“컴퓨터가 이해할 수 있는 코드는 바보도 작성할 수
있다. 사람이 이해할 수 있는 코드를 작성해라”
– 게으른 클래스(Lazy Class) – 마틴 파울러
– 추측성 일반화(Speculative Generality)
– 임시 필드(Temporary Field)
– 메시지 연쇄(Message Chains)
– 미들맨(Middle Man)
– 부적절한 친밀관계(Inappropriate Intimacy)
– 인터페이스가 다른 대체 클래스(Alternative Classes with Different Interfaces)
– 불완전한 라이브러리 클래스(Incomplete Library Class)
– 데이터 클래스(Data Class)
– 거부된 유산(Refused Bequest)
– 주석(Comments)
41
42. 긴 메소드 Long Method Smells
메소드의 길이가 짧은 프로그램이 오래 살아 남는다.
– 메소드의 길이가 길면 길수록 이해하기 더 어려워 진다.
– 그럼, 짧은 메소드의 기준은?
•의미상의 거리(Semantic Distance)를 측정
대부분 99%에 해당하는 경우
– Extract Method
조건문과 반복문들
– Decompose Conditional
42
43. 긴 메소드 Long Method Smells
Decompose Conditional이 필요한 다른 예
43
44. 이름 짓기 Naming Smells
addCourse( course );
getNodesArrayList();
makeIt();
getData();
sort();
processItem();
이름을 잘못 지었다고 확실히 와 닿는 경우도 있다.
W44()
44
45. 이름 짓기 Naming Smells
암기력에 의존하게 되는 학습을 강요하면 안 된다.
maskClear2( plMoney )
소스의 난이도를 높이는 형태의 불필요한 오버로딩 금지
System.out.println(LMoney.toCommaFormat(120000));
System.out.println(LMoney.toCommaFormat(120000, true)); // () 단위 표시를 추가시킨 스트링 반환
System.out.println(LMoney.toCommaFormat(070712093801.9283));
인자(argument)를 줄이기 위한 자기 참조 형태의 생성자 생성 자제
Money(String a) {
Money(String a, false); }
미스터리 소스 생성 금지
/**
* basic 한글 화폐단위를 담고 있는 Array
*/
private static final String basicA[] = { "", "십", "백", "천" };
/**
* base 한글 화폐단위를 담고 있는 Array
*/
private static final String baseA[] = { "", "만", "억", "조", "경" };
45
46. 주석 Comments Smells
코드 블록을 설명하기 위해 주석이 필요하다면?
– Extract Method
메소드 이름만으로 이해가 어려워 작성된 주석이라면?
– Rename Method
시스템의 상태를 가정하기 위해 기술된 문장이나 사전 조건을 명
시한 주석의 경우라면?
– Introduce Assertion
46
47. 주석 Comments Smells
주석이 나쁘다?
– 주석은 좋은 것이다. Sweet Smells
– But, 무엇(what)을 하는지 설명하는데 쓰여서는 안된다.
– 이유(why)를 설명하기 위해서 사용해야 한다.
– 잘못된 주석은 자칫 코드를 장황하게 만들거나 이해하기 어렵게 한다.
코드 블록을 설명하기 위해 코멘트가 필요하다면?
– Extract Method
47
48. 주석 Comments Smells
작성 가이드
– 코드가 하는 일을 그대로 주석으로 작성 (비권장)
– 코드의 설명 : 복잡하거니 미묘한 코드에 대한 내용을 주석으로 작성
(비권장, 차라리 코드를 수정)
– 표시기능 : Todo, Release 전에 할 일에 대한 내용을 주석으로 작성
(비권장, 적어도 배포 전에는 제거)
– 코드의 요약 : 몇 줄의 코드 뭉치가 하는 일을 요약하는 기능.
빠르게 코드를 살펴볼 수 있도록 도움을 줌(권장)
– 코드의 의도 설명 (권장)
– 코드만으로는 표현할 수 없는 정보: 저작권이나 버전번호, 최적화 내용 등을 기록(권장)
- from code complete
48
49. 주석 – Example #1 Smells
/**
*
* laf.xml 파일에 아래와 같이 세팅되어 있다고 가정한다.
*
* 1) test-mode
* 테스트로 메일을 발송 해보고자 할 경우 true로 지정하며, 이 경우 test-receivers 들에게 발송된다.
* 운영시에는 test-mode를 false로 지정한다.
*
* 2) spec
* spec 은 메일 발송에 필요한 mailer server정보와 발신자 정보 등을 기록한다.
* spec 에 name을 지정하지 않을 경우 default 값이 지정되며,
* default 로 지정된 것 중 일부만 변경하여 사용하고 싶을 경우에는 spec 의 name을 example 과 같이 정의한다.
* 이렇게 해 두면, default에서 지정한 기본값에 새로 지정한 값이 overwrite 된다.
* 즉, 공통적인 세팅은 default에 두고 변경하고자 할 경우만 spec에 name을 지정하여 사용할 수 있는 장점이 있다.
* spec 은 LMail 생성시 인자로서 지정하고, 인자가 없을 경우 default spec이 적용된다.
*
* 3) body-template
* html 형태로 메일을 보내고자 할 경우 사용한다.
* html string 을 직접 만들어서 send할 수도 있으나, 동적으로 html 내부 내용이 세팅되어야 할 경우에 유용하다.
* 이를 위해 template html 파일을 저장해두는 디렉토리와 html파일을 설정한다.
* mail-template 도 spec과 마찬가지로 LMail 생성시 인자로 지정한다.
* 인자가 없을 경우 default mail-template이 적용되고, sub-directory지정도 가능하다.
*
49
50. 주석 – Example #2 Smells
public static void writeMail(String[] args) {
// 메일에 사용할 유저 데이터를 LDATA를 이용하여 생성
LData ld = new LData("userInfo");
// 발송자의 이름을 생성한다.
// 단 발송자는 userName 이라는 항목으로 만들어야 함
ld.setString("userName", "Jubong, Park");
// 수신자 목록을 만든다. 수신자 항목은 LData에서 name 으로 설정한다.
LMultiData lm = new LMultiData("friends");
lm.add("name", "Hyori, Kim");
lm.add("name", "Yeesul, Lee");
lm.add("name", "Dongwook, Park");
// [ ] 로 수신인을 구분하므로, 스트링 버퍼를 사용하여 구분자가 첨부된 스트링으로 변환
StringBuffer imsi = new StringBuffer();
for(int i=0; i<lm.keySize("name"); i++) {
imsi.append("[");
imsi.append(lm.getString("name",i));
imsi.append("] ");
}
ld.setString("friends", imsi.toString());
50
51. 주석 – Example #2 Smells
public static void writeMail(String[] args) {
Mail mail = new Mail();
mail.setSender("Jubong, Park");
mail.addReceiver("Hyori, Kim");
mail.addReceiver("Yeesul, Lee");
mail.addReceiver("Dongwook, Park");
…
…
51
52. 주석 – Example #3 Smells
public static final int LowHandleLength = 400;
// 일반적인 경우 MaxTilt는 최저조정길이의 절반으로 설정한다.
public static final int MaxTilt = 200;
52
53. 기능에 대한 욕심 Feature Envy Smells
메소드 자신이 속한 클래스보다 다른 클래스에 대해 관심이 많은
메소드
– Move Method
다른 클래스를 이용해서 기능을 처리하는 특정 부분들
– Extract Method Move Method
함께 변경해야 하는 것들은 한 곳으로 모으자.
53
54. 긴 파라미터 리스트 Long Parameter List Smells
프로그래밍 초창기 세대의 규칙
– 전역변수를 쓰지 말자. 필요한 것들은 모두 파라미터로 넘기자.
파라미터 리스트가 길어지면?
– 이해하기 어렵다.
– 파라미터들의 관련성이나 일관성이 떨어진다.
54
55. 긴 파라미터 리스트 Long Parameter List Smells
OOP의 경우?
객체(Object)를 통해 필요한 정보들을 전달할 수 있다.
파라미터 리스트를 줄 일 수 있다!
– Replace Parameter with Method
– Preserve Whole Object
– Introduce Parameter Object
55
56. 확산적 변경 Divergent Change Smells
한 클래스가 어떤 이유로 인해 자주 변경되는 경우
– 소프트웨어는 변경하기 쉬워야 한다. 그리고 하나의 기능을 변경하기 위해
서 한 곳만 수정하도록 만들어야 한다. 그런데, 하나의 기능을 위해 한 클
래스 내부의 여러 곳을 수정해야 한다면?
특정 원인에 대해 변경해야 하는 부분들을 찾아내 묶자
– Extract Method
56
57. 임시 필드 Temporary Fields Smells
특정 상황에 따라 값이 변하게 되는 임시변수는 곧 잘 코드를 이
해하기 어렵게 만든다.
– Replace Temp with Query
– Replace Parameter with Method
임시 변수 대입이 빈번하다면?
– Extract Class
57
58. 중복 코드 Duplicate code Smells
수 많은 버그와 문제의 근원 = 중복 코드!!
– 중복 코드는 하나로 합치자!
동일한 표현이 여러 부분에 존재
– 같은 클래스 내부? Extract Method
– 하위 클래스들 사이에서? Extract Method and Pull Up Field
– 여러 클래스에 걸쳐서 나타나면? Extract Class
58
59. 스위치 구문 Switch statements Smells
잘 작성된 객체지향 프로그램의 확실한 특징 하나!
– 스위치(switch) 구문이 상대적으로 적다!
스위치 구문은 곧 잘 코드 중복을 유발하고 프로그램 내부 여러
곳에 비슷한 형태로 흩어져 존재하게 된다.
– Extract Method and Move Method
– Replace Type Code with Subclasses 혹은 Replace Type Code with
State/Strategy
– Replace Conditional with Polymorphism
– Replace Parameter with Explicit Methods
– Introduce Null Object
59
60. 산탄총 수술 Shotgun surgery Smells
하나의 기능 변경 위해 여러 클래스를 고쳐야 하는 경우
– 찾아내기 어렵고 수정할 부분들을 곧잘 놓치게 된다.
이상적으로는 추가/변경이 원투원(one-to-one) 링크형태로 이
뤄져야 한다.
– Move Method and Move Field
– Inline Class
60
61. 추측성 일반화 Speculative Generality Smells
“으흠! 이러이러한 기능이 나중에 필요할 걸! 미리 만들어 놓아야
지!”
– 하지만, 결과는 곧 잘 이해하기 어렵고 유지보수 하기 어렵게 만든다.
섣불리 미래를 예측하지 마라! 현재의 복잡도에 집중!
– 별 볼일없는 추상클래스? Collapse Hierarchy
– 불필요한 기능 위임이 발생한다면? Inline Class
– 지금 안 쓰는 파라미터라면? Remove Parameter
61
62. 기본형에 대한 강박 Primitive Obsession Smells
객체 지향 프로그래밍에서는 기본형과 클래스를 애써 구별 지을
필요 없다.
객체지향적인 사고로 전환하자!
– 이름, 화폐, 통화단위, 전화번호, 우편번호, 주소, 나이 등등 모든 것이 객
체가 될 수 있다.
– Replace Data Value with Object
– Replace Array with Object
62
63. 3. 리팩터링 카탈로그
1. Extract Method 13. Replace Type Code with Subclasses
2. Rename Method 14. Replace Type Code with
State/Strategy
3. Introduce Assertion
15. Replace Conditional with
4. Move Method
Polymorphism
5. Decompose Conditional
16. Introduce Null Object
6. Move Field
17. Preserve Whole Object
7. Extract Class
18. Collapse Hierarchy
8. Inline Class
19. Remove Parameter
9. Replace Temp with Query
20. Introduce Parameter Object
10. Pull Up Field ↔ Pull Down Field
21. Replace Data Value with Object
11. Replace Parameter with Method
22. Replace Array with Object
12. Replace Type Code with Class
63
리팩터링
64. 리팩터링 카탈로그 #1 리팩터링 카탈로그
– Extract Method http://www.refactoring.com/catalog/
– Decompose Conditional
– Rename Method
– Introduce Assertion
– Move Method
– Move Field
– Extract Class
– Inline Class
– Replace Temp with Query
– Pull Up Field
– Replace Parameter with Method
64
65. 리팩터링 카탈로그 #2 리팩터링 카탈로그
– Replace Type Code with Class
– Replace Type Code with Subclasses
– Replace Type Code with State/Strategy
– Replace Conditional with Polymorphism
– Introduce Null Object
– Preserve Whole Object
– Collapse Hierarchy
– Remove Parameter
– Introduce Parameter Object
– Replace Data Value with Object
– Replace Array with Object
65
66. Extract Method 리팩터링 카탈로그
코드 조각이 여기저기 흩어져 있을 때
- 메소드가 길거나, 주석이 필요할 때 사용하며, 재사용, 가독성을 높여준다.
- 이름을 잘 지어야 하며, 메소드 추출의 핵심은 “의미상의 거리(Semantic distance)”이다.
66
67. Rename Method 리팩터링 카탈로그
이름만으로 메소드 의도 파악이 잘 안될 때
Customer Customer
getinvcdtlmt() getInvoiceableCreditLimit()
- 메소드는 작성 의도를 잘 전달해 주는 이름을 가져야 한다.
- 글로 작성하는 코드는 사람을 이해시키는 것이 목적이지, 컴퓨터를 이해시키는 것이 아니다.
- 진정으로 뛰어난 프로그래머가 되고 싶은가? 무엇보다 이름을 잘 짓기 위해 노력하고 연습해라!
67
68. Introduce Assertion 리팩터링 카탈로그
코드의 일부가 프로그램의 특정 상태를 가정하고 있을 때
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();
}
- 명시적으로 체크하는 구문을 작성하거나 값을 확인 한다.
68
69. Move Method 리팩터링 카탈로그
메소드가 자신이 정의된 클래스보다 다른 클래스의 기능을 더 많이 사용할 때
- 클래스가 너무 많은 동작을 가지고 있거나, 다른 클래스와 공동으로 일하는 부분이 너무 많아서 단단히 결합되어 있
을 때 메소드 위치를 옮긴다.
- 만약 메소드를 옮겨야 할지에 대한 확신이 없다면 다른 메소드들을 함께 살펴보자.
69
70. Decompose Conditional 리팩터링 카탈로그
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);
70
71. Move Field 리팩터링 카탈로그
필드가 자신이 정의된 클래스보다 다른 클래스에 의해 더 많이 사용 될 때
71
72. Extract Class 리팩터링 카탈로그
두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우
- 일반적으로 클래스는 시간이 지날수록 커지게 되며, 크기가 커질 수록 이해하기 어려워진다.
- 이럴 경우, 쪼갤 수 있다면, 쪼개라!
72
73. Inline Class 리팩터링 카탈로그
클래스가 하는 일이 별로 없을 때
- 흔히 리팩터링 할 때 메소드를 옮기고 난 다음 살펴보니, 이젠 해당 클래스가 하는 일이 별로 없다는 걸 알게 된다.
73
74. Replace Temp with Query 리팩터링 카탈로그
어떤 수식의 결과값을 저장하기 위해서 임시변수를 사용하고 있을 경우
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98; if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
...
double basePrice() {
return _quantity * _itemPrice;
}
- 임시 변수는 임시로 사용되고, 특정 부분에서만 의미를 가지므로 문제가 된다.
- 임시변수가 사용되는 메소드는 보통 길이가 길어진다.
74
75. Pull Up Field ↔ Pull Down Field 리팩터링 카탈로그
두 개의 하위 클래스가 같은 필드를 갖고 있을 경우
- 중복을 제거한다.
75
76. Replace Parameter with Method 리팩터링 카탈로그
메소드로 받은 결과를 다른 메소드의 파라미터로 넘겨 주는 경우
int basePrice = _quantity * _itemPrice;
discountLevel = getDiscountLevel();
double finalPrice = discountedPrice (basePrice, discountLevel);
int basePrice = _quantity * _itemPrice;
double finalPrice = discountedPrice (basePrice);
- 파라미터로 넘겨주는 메소드에서도 호출 할 수 있는 메소드라면 호출 결과를 받아서 넘겨 줄 필요가 없다.
76
77. Replace Type Code with Class 리팩터링 카탈로그
어떤 클래스가 동작에 영향을 미치지 않는 숫자 형식 타입코드를 가졌을 경우
- 타입 코드가 클래스의 동작에 영향을 주는지 여부를 고려해야 한다.
- 타입 코드를 나타내는 변수가 단순 별칭이 아니라 컴파일러가 타입을 체크해 줄 수 있도록 만든다.
77
78. Replace Type Code with Subclasses 리팩터링 카탈로그
클래스의 행위에 영향을 주는 변경 불가능한(immutable) 타입코드를 가질 경우
- 만약 타입 코드가 클래스의 행위에 영향을 줄 경우, 최선의 방법은 다형성을 이용하는 것이다.
- 다양한 행위에 대한 지식을 클라이언트 코드에서 특정 클래스로 위임하게 된다는 것이 장점이다.
78
79. Replace Type Code with State/Strategy 리팩터링 카탈로그
행동에 영향을 주는 타입 코드가 있지만 해당 타입 코드를 서브클래스로 만들 수
없는 경우
- 객체가 살아있는 동안 타입 코드에 따라 행위가 바뀌도록 만들 수 있다.
- 컴파일러에 의한 타입 체크 지원을 받을 수 있다.
79
80. Replace Conditional with
리팩터링 카탈로그
Polymorphism #1
객체의 타입에 따라 다른 동작을 선택하는 조건문을 가지고 있는 경우
double getSpeed() {
switch (_type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
case NORWEIGIAN_BLUE:
return (_isNailed) ? 0 : getBaseSpeed(_voltage);
}
throw new RuntimeException ("Should be unreachable");
}
- 조건문의 각 부분을 서브클래스에 있는 오버라이딩 메소드로 옮기고 원래 메소드를 abstract로 만든다.
80
82. Introduce Null Object 리팩터링 카탈로그
반복적인 널(null) 검사가 이루어지고 있을 경우
if (customer == null)
plan = BillingPlan.basic();
else
plan = customer.getPlan();
- null 값을 null 객체로 대체한다.
- 다형성의 진가는, 객체에 타입을 묻고 그 답을 기초로 하여 어떤 동작을 호출하는 대신에 단지 그 동작을 호출하는
것이다. 그러면 그 객체는 타입에 따라서 적절히 동작한다.
82
83. Preserve Whole Object 리팩터링 카탈로그
어떤 객체에서 여러 개의 값을 얻은 다음 메소드 호출 시 파라미터로 넘길 경우
int low = daysTempRange().getLow();
int high = daysTempRange().getHigh();
withinPlan = plan.withinRange(low, high);
withinPlan = plan.withinRange(daysTempRange());
- 메소드로 넘겨야 하는 값이 변경되는 경우, 해당 메소드들을 다 고쳐야 하는 상황이 발생한다. 관련 데이터를 객체로
만들어 넘겨주면 이런 상황에도 메소드의 외형을 유지시켜 준다.
83
84. Collapse Hierarchy 리팩터링 카탈로그
상위 클래스와 하위 클래스가 크게 다르지 않을 경우
- 리팩터링을 진행하는 과정 도중에 종종 나타난다.
84
85. Remove Parameter 리팩터링 카탈로그
파라미터가 더 이상 메소드 안에서 쓰이지 않을 경우
- 쓰지 않는 파라미터를 남겨 놓으면 다른 사용자는 고민한다.
- 단, 다형성에 사용되는 메소드일 경우, 파라미터 제거에 좀 더 신중해야 한다.
85
86. Introduce Parameter Object 리팩터링 카탈로그
함께 뭉쳐 다니는 파라미터들의 그룹이 있을 경우
- 클래스로 도출해 내면 복잡도가 감소된다.
- 또한, 관련된 동작도 해당 클래스 내부로 옮기는 것이 가능해지므로 중복코드도 줄어들게 된다.
86
87. Replace Data Value with Object 리팩터링 카탈로그
추가적인 데이터나 행위가 필요한 데이터 항목을 가질 경우
- 예를 들면, 전화번호를 처음 한동안은 문자열(String)으로 표현 할 수 있지만, 나중에는 포맷팅을 해야 한다든가, 지
역 코드를 추출해야 하는 등의 특별한 동작이 필요해지는 경우가 발생한다.
87
88. Replace Array with Object 리팩터링 카탈로그
배열의 특정 요소가 다른 뜻을 가지고 있을 경우
String[] row = new String[3];
row [0] = "Liverpool";
row [1] = "15";
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
88