varplace=location_info["LocalityName"]; // e.g. "Santa Monica"
if (!place) {
place=location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
}
if (!place) {
place=location_info["AdministrativeAreaName"]; // e.g. "California"
}
if (!place) {
place="Middle-of-Nowhere";
}
if (location_info["CountryName"]) {
place+=", "+location_info["CountryName"]; // e.g. "USA"
} else {
place+=", Planet Earth";
}
returnplace;
예제 코드에서는 비어있는 값을 다루기 위해서 기본값을 주는 방법을 사용했다.
'도시'를 선택할 때 값이 있으면 '도시/마을'을 사용한다. 만약 이 값이 없으면 '도시권/자치주'를 사용하고, 이도 없으면 '주/영역'을 사용한다.
세 값이 모두 없으면 '도시/마을'에 "Middle-of-Nowhere(아무 곳도 아닌 곳)'이라는 기본값을 준다.
만약 '나라'값이 없으면, 기본값으로 "Planet Earth(지구)"라는 값을 준다.
무작정 기본값을 주는 코드를 추가한 예제 코드는 지저분해졌고, 기능을 더 추가하면 더 지저분해질 상황이 되었다.
그래서 책에서는 '한 번에 한 가지 일' 원리를 적용하기 위해서 코드가 하는 일을 모두 정리했다.
location_info 딕셔너리에서 값들을 읽는다.
1
2
3
4
vartown=location_info["LocalityName"]; // e.g. "Santa Monica"
varcity=location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
varstate=location_info["AdministrativeAreaName"]; // e.g. "CA"
varcountry=location_info["CountryName"]; // e.g. "USA"
'도시'의 값을 설정하기 위해 정해진 선호도 순으로 값을 읽는다. 아무런 값도 찾을 수 없으면 "아무 곳도 아닌 곳"이라는 기본값을 설정한다.
1
2
3
4
5
6
7
8
// Start with the default, and keep overwriting with the most specific value.
varsecond_half="Planet Earth";
if (country) {
second_half=country;
}
if (state&&country==="USA") {
second_half=state;
}
'나라' 값을 설정한다. 값이 없으면 기본값인 "지구"로 설정한다.
1
2
3
4
5
6
7
8
9
10
varfirst_half="Middle-of-Nowhere";
if (state&&country!=="USA") {
first_half=state;
}
if (city) {
first_half=city;
}
if (town) {
first_half=town;
}
place를 변경한다.
1
returnfirst_half+", "+second_half;
- 코드를 리팩토링할 때 보통 여러 가지 접근 방법이 있다.
- 일단 몇 가지 작업을 분리하면, 코드는 더 생각하기 쉬워지므로 이를 리팩토링 할 수 있는 더 나은 방법이 떠오르게 될 수도 있다.
// WARNING: DO NOT STARE DIRECTLY AT THIS CODE FOR EXTENDED PERIODS OF TIME.
voidUpdateCounts(HttpDownloadhd) {
// Figure out the Exit State, if available.
if (!hd.has_event_log() ||!hd.event_log().has_exit_state()) {
counts["Exit State"]["unknown"]++;
} else {
stringstate_str=ExitStateTypeName(hd.event_log().exit_state());
counts["Exit State"][state_str]++;
}
// If there are no HTTP headers at all, use "unknown" for the remaining elements.
if (!hd.has_http_headers()) {
counts["Http Response"]["unknown"]++;
counts["Content-Type"]["unknown"]++;
return;
}
HttpHeadersheaders=hd.http_headers();
// Log the HTTP response, if known, otherwise log "unknown"
if (!headers.has_response_code()) {
counts["Http Response"]["unknown"]++;
} else {
stringcode=StringPrintf("%d", headers.response_code());
counts["Http Response"][code]++;
}
// Log the Content-Type if known, otherwise log "unknown"
if (!headers.has_content_type()) {
counts["Content-Type"]["unknown"]++;
} else {
stringcontent_type=ContentTypeMime(headers.content_type());
counts["Content-Type"][content_type]++;
}
}
이번 예제는 더 길고 복잡한 코드이다.
이 예제는 길고 복잡한만큼 실제로 많은 논리를 포함하며, 심지어 반복되는 코드도 있다. 이런 코드를 읽는 것은 결코 즐겁지 않다.
voidUpdateCounts(HttpDownloadhd) {
// Task: define default values for each of the values we want to extract
stringexit_state="unknown";
stringhttp_response="unknown";
stringcontent_type="unknown";
// Task: try to extract each value from HttpDownload, one by one
if (hd.has_event_log() &&hd.event_log().has_exit_state()) {
exit_state=ExitStateTypeName(hd.event_log().exit_state());
}
if (hd.has_http_headers() &&hd.http_headers().has_response_code()) {
http_response=StringPrintf("%d", hd.http_headers().response_code());
}
if (hd.has_http_headers() &&hd.http_headers().has_content_type()) {
content_type=ContentTypeMime(hd.http_headers().content_type());
}
// Task: update counts[]
counts["Exit State"][exit_state]++;
counts["Http Response"][http_response]++;
counts["Content-Type"][content_type]++;
}
이렇게 작업을 정리하고 다시 작성한 코드는 다음과 같은 목적을 가진 세 개의 영역으로 분리되었다.
읽고자 하는 각각의 값을 위한 기본값을 정의한다. (우리가 관심을 가지고 있는 세 개의 키에 기본값을 설정한다.)
HttpDownload에서 각각의 값을 하나씩 읽는다. (각 키에 대한 값이 존재하면 그 값을 읽어서 문자열로 변환한다.)
count[]를 갱신한다. (각각의 키/값 짝에 counts[]를 갱신한다.)
처음에 나열한 작업이 네 개인데 실제 코드에는 세 개의 영역만 존재하는데 문제 될 것은 전혀 없다.
처음에 나열한 작업은 그저 출발점에 해당할 뿐이다. 나열된 대상 중에서 일부만 분리되어도 큰 도움이 된다.
이러한 개선은 별도로 함수를 만들지 않더라도 코드 정리를 도와준다. 예제에서는 처음에 별도의 함수를 만들지 않고 코드를 정리했다.
개선은 여러가지 방법으로 행할 수 있으며, 예제에서는 함수를 사용하는 방법도 따로 보여준다.
함수를 따로 정리한 예제 코드에는 중간 결과값을 저장하는 변수가 제거되었으며, 이는 9장과 관련이 있다.
두 해결책 모두 코드를 읽는 사람이 한 번에 하나의 일만 생각하게 하므로 가독성을 높여준다.
__요약
This chapter illustrates a simple technique for organizing your code: do only one task at a
time.
If you have code that’s difficult to read, try to list all of the tasks it’s doing. Some of these tasks might easily become separate functions (or classes). Others might just become logical “paragraphs” within a single function. The exact details of how you separate these tasks isn’t as important as the fact that they’re separated. The hard part is accurately describing all the little things your program is doing.