최근에 이해하기 어렵지 않으면서 실무에 바로 도움이 될 수 있는 책을 읽은 것 같아서 짧게 요약해 봤다.
나같은 주니어라면 이 책이 정말 도움이 되지 않을까 싶었다. 코드를 짜면서 한 번씩은 고민해봤을 주제들이 다 있는 듯 하다.
반납하기 전에 급하게 요약한거라... 미학적으로 보기 좋지 않아도 이해 바란다. 나중에 더 예쁘게 고치겠다(대체 언제??)
요약 시작!
대전제: 1.코드는 이해하기 쉬워야 한다.
#코드는 다른 사람이 그것을 이해하는 데 들이는 시간을 최소화하는 방식으로 작성되어야 한다.
#적은 분량으로 코드를 작성하는 것이 좋은 목표긴 하지만, 이해를 위한 시간을 최소화하는 게 더 좋은 목표다.
Part One 표면적 수준에서의 개선
2. 이름에 정보 담기
#이름에 정보를 담아내라
*특정한 단어 고르기
- BinaryTree 클래스에서 트리 노드의 개수를 반환하는 메서드는 size()보다 numNodes()가 낫다.
- 유의어 색인집을 찾아보거나 동료에게 더 나은 이름을 묻는 일을 주저하면 안 된다.
*보편적인 이름 피하기(혹은 언제 그런 이름을 사용해야 하는지 깨닫기)
- tmp라는 이름은 대상이 짧게 임시적으로만 존재하고, 임시적 존재 자체가 변수의 가장 중요한 용도일 때에 한해서 사용해야 한다.
- run_locally 라는 명령형 플래그 대신 extra_logging와 use_local_database로 쪼개는 게 의미가 더 명확하고 유연한 선택을 제공한다.
*추상적인 이름 대신 구체적인 이름 사용하기
*접두사 혹은 접미사로 이름에 추가적인 정보 덧붙이기
*이름이 얼마나 길어져도 좋은지 결정하기
- 좁은 범위에서는 짧은 이름이 괜찮다.
- 흔히 쓰는 약어와 축약형은 괜찮지만 본인만 알아볼 수 있는 축약형은 안 된다.
- 불필요한 단어 제거하기
*추가적인 정보를 담을 수 있게 구성하기
3. 오해할 수 없는 이름들
#경계를 포함하는 한계값을 다룰 때는 min과 max를 사용하라
#경계를 포함하는 범위에는 first와 last를 사용하라
#경계를 포함하고/배제하는 범위에는 begin과 end를 사용하라
#사용자의 기대에 부응하기
- get으로 시작하는 메소드는 왠지 금방 값을 반환할 거 같은 느낌을 준다. 사실 값을 계산하는데 리소스가 많이 든다면 computeMean() 이런식으로 변환하는게 낫다.
# 이름을 짓기 위해서 복수의 후보를 평가하기
4. 미학
#미학적으로 보기 좋은 코드가 사용하기 더 편리하다
#메소드를 활용하여 불규칙성을 정리하라
assert(ExpandFullName(database_connection, “Doug Adams”, &error == “Mr. Douglas Adams”);
assert(error == “”);
assert(ExpandFullName(database_connection, “Jake Brown”, &error== “Mr. Jake Brown III”);
assert(error == “no match found”);
위의 코드를
CheckFullName(“Doug Adams”, “Mr.Douglas Adams”, “”);
CheckFullName(“Jake Brown”, “Mr. Jake Brown III”, “”);
이렇게 변경 가능하다.
#도움이 된다면 코드의 열을 맞춰라
#의미있는 순서를 선택하고 일관성 있게 사용하라
- 가장 중요한 것에서 시작해서 가장 덜 중요한 것까지 순서대로 나열하라
# 선언문을 블록으로 구성하라
# 코드를 ‘문단’으로 쪼개라
# 일관성 있는 스타일은 ‘올바른’ 스타일보다 더 중요하다
5. 주석에 담아야 하는 대상
#주석의 목적은 코드를 읽는 사람이 코드를 작성한 사람만큼 코드를 잘 이해하게 돕는 데 있다.
#코드에서 빠르게 유추할 수 있는 내용은 주석으로 달지 말라
#나쁜 이름에 주석을 달지 마라 – 대신 이름을 고쳐라
#’감독의 설명’을 포함할
#코드에 있는 결함을 설명하라
#상수에 대한 설명이 필요하면 포함해도 좋다
//합리적인 한계를 설정하라 – 그렇게 많이 읽을 수 있는 사람이 없다.
Const int MAX_RSS_SUBSCRIPTIONS = 1000;
#코드를 읽는 사람의 입장이 되어라
#사람들이 쉽게 빠질 것 같은 함정을 경고하라
#글(주석) 쓰는 두려움을 떨쳐내라
6. 명확하고 간결한 주석 달기
#주석을 간결하게 하라
#모호한 대명사는 피하라
#엉터리 문장을 다듬어라
#함수의 동작을 명확하게 설명하라
#구체적인 용법을 설명해주는 입/출력 예를 사용하라
// 예: Strip(“ab”, “a”)은 “b”를 반환한다.
#코드의 의도를 명시하라
#정보 축약형 단어를 사용하라
//이 클래스는 데이터베이스에 대한 캐시 계층으로 기능한다.
Part Two 루프와 논리를 단순화하기
7. 읽기 쉽게 흐름제어 만들기
# 흐름을 제어하는 조건과 루프 그리고 여타 요소를 최대한 ‘자연스럽게’ 만들도록 노력하라. 코드를 읽다가 다시 되돌아가서 코드를 읽지 않아도 되게끔 만들어야 한다.
#부정이 아닌 긍정을 다루어라
If(!debug)가 아니라 if(debug)를 선호하라
#간단한 것을 먼저 처리하라
#더 흥미롭고, 확실한 것을 먼저 다루어라
#줄 수를 최소화하는 일보다 다른 사람이 코드를 읽고 이해하는 데 걸리는 시감을 최소화하는 일이 더 중요하다.
#함수 중간에서 반환하는 것은 완전히 허용되어야 한다. 함수 중간에서 반환하여 중첩을 제거하라.
#루프 내부에 있는 중첩 제거하기
8. 거대한 표현을 잘게 쪼개기
#드모르간의 법칙 사용하기
1) not (a or b or c ) == (not a) and (not b) and (not c)
2) not (a and b and c ) == (not a) or (not b) or (not c)
- 위아래 if문은 뜻이 똑같다.
if(!(file_exists && !is_protected))
if(file_exists && is_protected)
#더 우아한 접근방법 발견하기
Ex) 주어진 범위의 양쪽 경계값이 other의 범위에 속하는지 확인하는 OverlapsWith()함수를 구현하는 2가지 방법
1) boolean OverLapsWith(Range other){
return (begin >= other.begin && begin < other.end) ||
(end > other.begin && end <= other.end) ||
(begin <= other.begin && end >= other.end);
}
2) boolean OverLapsWith(Range other){
if(other.end <= begin) return false; // 우리가 시작하기 전에 끝난다.
if(other.begin >= end) return false; // 우리가 끝난 후에 시작한다.
return true; // 마지막 가능성만 남았다. 즉 겹친다.
}
2번째 방법이 더 간단!
9. 변수와 가독성
#불필요한 임시 변수들, 중간 결과담는 변수 삭제
#변수의 범위를 좁혀라
#많은 메소드를 정적 static으로 만들라
#커다란 클래스를 여러 작은 클래스로 나누라
#정의를 사용하는 곳에 가깝게 옮겨라
#값을 한 번만 할당하는 변수를 선호하라
(변수값이 달라지는 곳이 많아지는 것보다 변수의 숫자가 더 많아지는 게 나은 듯)
PART THREE 코드 재작성하기
10. 상관없는 하위문제 추출하기
#순수한 유틸리티 코드를 빼내서 만든다.
#util 패키지에 담아놓을 수 없는 특정한 프로젝트를 위한 기능이더라도 따로 빼는 것이 나으면 따로 빼서 같은 클래스에 담아놓아도 괜찮다.
# 자신의 필요에 맞춰서 인터페이스의 형태를 바꾸기
# 지나치게 추출하는 건 지양하기
11. 한 번에 하나씩
#한 번에 하나의 작업만 수행하게 코드를 구성해야 한다.
(이전 코드는 너무 길어서…개선된 코드 일부만 적어놓는다)
void UpdateCounts(HttpDownload hd){
//작업: 읽고자 하는 각각의 값을 위한 기본값을 정의한다.
String exit_state = “unknown”;
String http_response = “unknown”;
//작업: HttpDownload에서 각각의 값을 하나씩 읽는다.
if(hd.has_event_log() && hd.event_log().has_exit_stage()){
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().reponse_code()));
}
//작업: count[]를 갱신한다.
counts[“Exit State”][exit_state]++;
counts[“Http Response”][http_response]++;
}
12. 생각을 코드로 만들기
“할머니에게 설명할 수 없다면 당신은 제대로 이해한 게 아닙니다”
- 알버트 아인슈타인
#논리를 명확하게 설명하기
#라이브러리를 알면 도움이 된다.
#자신의 문제를 쉬운 말로 설명할 수 없으면 해당 문제는 무언가 빠져 있거나 아니면 제대로 정의되지 않은 것이다. 어떤 프로그램을 혹은 어떤 생각이라도 말로 설명하는 행위는 문제의 틀을 제대로 잡는 데 도움을 준다.
13. 코드 분량 줄이기
#필요한 기능이 아니면 그 기능을 구현하려고 애쓰지 마라
- 코드베이스에 새로운 ‘무게’를 더하는 데 얼마나 많은 시간이 필요한지 잊지마라
#요구사항에 질문을 던지고 질문을 잘게 나누어 분석하라
- 복잡한 LRU 캐시를 구현하는 대신 단순하게 가장 최근 1개 항목만 저장하는 캐시를 구현해서 약 90%의 실행속도 향상을 이룰 수도 있다
#코드베이스를 최대한 작고 가볍게 유지하라
#자기 주변에 있는 라이브러리에 친숙해져라
- 매일 15분씩 자신의 표준 라이브러리에 있는 모든 함수/모듈/형들의 이름을 읽어라. 코드를 직접 작성하는 대신 “잠깐 전에 API에서 보았던 뭔가 비슷한데..”라고 생각나면 우선 존재하는 라이브러리를 사용하는 습관을 가질 수 있다.
- 코딩 대신 유닉스 도구를 활용도 가능
PART FOUR 선택된 주제들
14. 테스트와 가독성
#읽거나 유지보수하기 쉽게 테스트를 만들어라
#최소한의 테스트 구문 만들기
- 테스트 값을 넣기 쉽게 이를 위한 메서드를 작성
#테스트하기 어려운 코드의 특징
1) 전역변수 사용
2) 코드가 많은 외부 컴포넌트 사용
3) 코드가 비결정적인 행동을 한다
#테스트하기 쉬운 코드의 특징
1) 클래스들이 내부 상태를 거의 가지고 있지 않다
2) 클래스/함수가 한 번에 하나의 일만 수행
3) 클래스가 다른 클래스에 의존하지 않고 서로 상당히 떨어져 있다
4) 함수들이 간단하고 잘 정의된 인터페이스를 가지고 있다
#지나치게 테스트에 집착하면 안 된다.
- 테스트를 가능하게 하려고 실제 코드의 가독성을 희생시키면 안 된다
'개발잡담' 카테고리의 다른 글
Isolation level 변경하면 어떤 결과가 나올까 (0) | 2024.01.09 |
---|---|
Clean Architecture 요약 (0) | 2022.05.13 |
Generic(제네릭) 에 대해서 (0) | 2022.04.13 |
PRG(POST-REDIRECT-GET) 패턴 (0) | 2022.01.02 |
Transaction은 뭐고 @Transactional은 언제 쓰는 건데? (0) | 2021.12.26 |
댓글