김주엽
1. 📌 핵심 개념 정리
✅ 요약하기
- 주석
- 부적절한 정보
- 변경 이력과 같은 주석은 적절하지 못하다.
- 일반적으로 작성자, 최종 수정일, SPR(Software Problem Repost) 번호 등과 같은 메타 정보를 주석으로 남긴다.
- 주석은 코드와 설계에 기술적인 설명을 부연하는 수단이다.
- 쓸모 없는 주석
- 오래된 주석, 엉뚱한 주석, 잘못된 주석은 쓸모가 없다.
- 쓸모 없어질 주석을 작성하지 않는 것이 가장 좋지만 작성했다면 재빨리 삭제하는 것이 좋다.
- 쓸모 없는 주석은 코드를 그릇된 방향으로 이끈다.
- 중복된 주석
- 코드만으로 충분한데 구구절절 설명하는 주석을 중복된 주석이라고 한다.
- 예시
i++; // i 증가
- 예시
- 함수 서명만 달랑 기술하는
Javadoc
주석- 예시
/** * @param sellRequest * @return * @throws ManagedComponentException * **/ public SellResponse beginSellItem(SellRequest sellRequest) throws ManagedComponentException { }
- 예시
- 코드만으로 충분한데 구구절절 설명하는 주석을 중복된 주석이라고 한다.
- 주석 처리된 코드
- 코드를 읽다가 주석으로 처리된 코드가 나오면 아주 거슬린다.
- 오래된 코드인지 중요한 코드인지 구분할 수 없기에 아무도 삭제하지 않는다.
- 누군가에게 필요하거나 다른 사람이 사용할 코드라 판단하기 때문이다.
- 주석으로 처리된 코드는 읽는 사람을 헷갈리게 만든다.
- 이전 코드는 코드 관리 시스템이 기억하기에 걱정말고 바로 지우도록 하자.
- 부적절한 정보
- 환경
- 여러 단계로 빌드해야 한다.
- 빌드는 간단히 한 단계로 끝나야 한다.
- 불가해한 명령이나 스크립트를 실행해 각 요소를 따로 빌드할 필요가 없어야 한다
- 시스템에 필요한 파일을 찾느라 여기저기 찾으러 다닐 필요가 없어야 한다.
- 한 명령으로 전체를 체크아웃해서 빌드할 수 있어야 한다.
- 여러 단계로 테스트해야 한다.
- 모든 단위 테스트는 한 명령으로 돌려야 한다.
- IDE에서 버튼 하나로 테스트가 가능한 것이 가장 이상적이다.
- 아무리 열악한 환경이라도 셸에서 명령 하나로 가능해야 한다.
- 모든 단위 테스트는 한 명령으로 돌려야 한다.
- 여러 단계로 빌드해야 한다.
- 함수
- 너무 많은 인수
- 함수에서 인수는 작을수록 좋고 아예 없으면 가장 좋다.
- 넷 이상은 그 가치가 아주 의심스럽기에 최대한 피한다.
- 출력 인수
- 일반적으로 독자는 인수를 입력으로 간주한다.
- 함수에서 뭔가의 상태를 변경해야 한다면 함수가 속한 객체의 상태를 변경한다.
- 플래그 인수
boolean
인수는 함수가 여러 기능을 수행한다는 명백한 증거다.- 플래그 인수는 혼란을 초래하므로 피하라.
- 죽은 함수
- 아무도 호출하지 않는 함수는 과감하게 삭제한다.
- 소스 코드 관리 시스템이 모두 기억하기에 걱정할 필요 없다.
- 너무 많은 인수
- 일반
- 한 소스 파일에 여러 언어를 사용한다.
- 오늘날 프로그래밍 환경은 한 소스 파일 내에서 다양한 언어를 지원한다.
- 자바 소스 파일에서 XML, HTML, YAML, Javadoc 등을 포함한다.
- JSP 파일에서 HTML, 자바, 태그 라이브러리 구문, 영어 주석, JavaScript 등을 포함한다.
- 이는 좋게 말하면 혼란스럽고 나쁘게 말하면 조잡하다.
- 한 소스 파일에 언어 하나만 사용하는 방식이 가장 좋다.
- 현실적으로 어렵다면 소스 파일에서 언어 수와 범위를 최대한 줄이도록 한다.
- 오늘날 프로그래밍 환경은 한 소스 파일 내에서 다양한 언어를 지원한다.
- 당연한 동작을 구현하지 않는다.
- 최소 놀람의 원칙(The Principle of Least Surprise)에 의거해 함수나 클래스는 당연한 동작과 기능을 제공한다.
- 당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 사람이 함수 기능을 예상하기 어렵다.
- 저자를 신뢰하기 어려워 코드를 모두 살펴보게 된다.
- 경계를 올바로 처리하지 않는다.
- 대부분의 개발자가 머릿속에서 코드를 돌려보고 끝낸다.
- 자신의 직관에 의존할 뿐 모든 경계와 구석진 곳에서 코드를 증명하려 애쓰지 않는다.
- 모든 경계 조건을 찾아내고 모든 경계 조건을 테스트하는 테스트 케이스를 작성하라.
- 중복
- 이 책에서 나오는 가장 중요한 규칙 중 하나로 DRY(Don't Repeat Yourself) 원칙이라 부른다.
- 익스트림 프로그래밍의 핵심 규칙 중 하나로 론 제프리스는 모든 테스트를 통과한다는 규칙 다음으로 중요하게 꼽았다.
- 코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라.
- 중복된 코드를 하위 루틴이나 다른 클래스로 분리하라.
- 가장 흔한 유형은 똑같은 코드가 여러 차례 나오는 중복이다.
- 개발자가 마우스로 긁어다 여기저기로 복사한 듯 보이는 코드를 말한다.
- 이런 중복은 간단한 함수로 교체한다.
- 좀 더 미묘한 유형은 여러 모듈에서 일련의
switch
나if
문으로 같은 조건을 확인하는 중복이다.- 다형성을 이용해서 대체하라.
- 마지막 유형으로는 알고리즘은 유사하나 코드가 서로 다른 중복이다.
Template Method
혹은Strategy
패턴으로 중복을 제거한다.
- 최근 15년 동안 나온 디자인 패턴은 대다수가 중복을 제거하는 패턴이다.
- 추상화 수준이 올바르지 못한다.
- 추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다.
- 모든 저차원 개념은 파생 클래스에 넣고 모든 고차원 개념은 기초 클래스에 넣는다.
- 세부 구현과 관련된 상수, 변수, 유틸리티 함수는 기초 클래스에 넣으면 안 된다.
- 기초 클래스는 구현 정보에 대해 알 수 없어야 한다.
- 고차원 개념과 저차원 개념을 섞는 경우를 피하라.
- 예시
public interface Stack { Object pop() throws EmptyException; void push(Object o) throws FullException; double percentFull(); class EmptyException extends Exception {} class FullException extends Exception {} }
percentFull
함수는 추상화 수준이 올바르지 못하다. Stack을 구현하는 방법은 다양하기에BoundedStack
과 같은 파생 인터페이스에 넣어야 마땅하다.
- 예시
- 잘못된 추상화는 임시변통으로 고치기는 불가능하다.
- 기초 클래스가 파생 클래스에 의존한다.
- 기초 클래스가 파생 클래스를 사용한다면 문제가 있다는 말이다.
- 일반적으로 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다.
- 간혹 파생 클래스 개수가 확실히 고정되었다면 기초 클래스에 파생 클래스를 선택하는 코드가 들어간다.
- FSM(Finite State Machine) 구현에서 많이 볼 수 있는 사례다.
- 그러나 FSM은 기초 클래스와 파생 클래스가 굉장히 밀접하고 언제나 같은 JAR 파일로 배포한다.
- 과도한 정보
- 잘 정의된 모듈은 인터페이스가 아주 작다.
- 잘 정의된 인터페이스는 많은 함수를 제공하지 않아 결합도가 낮다.
- 우수한 개발자라면 클래스나 모듈 인터페이스에 노출할 함수를 제한할 줄 알아야 한다.
- 클래스가 제공하는 메서드 수는 작을수록 좋다.
- 함수가 아는 변수, 클래스의 인스턴스 변수 수도 작을수록 좋다.
- 자료, 유틸리티 함수, 상수와 임시 변수를 숨겨라.
- 하위 클래스에서 필요하다는 이유로
protected
변수, 함수를 마구 생성하지 마라. - 인터페이스를 매우 작고 깐깐하게 만들어서 결합도를 낮춰라.
- 일관성 부족
- 어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다.
- 예를 들어 한 함수에서
response
라는 변수에HttpServletResponse
를 저장했다면 다른 함수에서도 일관성 있게 동일한 변수 이름 혹은 유사한 이름을 사용하라.
- 기능 욕심
- 클래스 메서드는 자기 클래스의 변수와 메서드에 관심을 가져야지 다른 클래스의 변수, 함수를 참조해서는 안 된다.
- 메서드가 다른 객체의 참조자와 변경자를 사용해 그 객체를 조작하는 것을 말한다.
- 서술적 변수
- 프로그램 가독성을 높이는 가장 효과적인 방법 중 하나가 서술적인 변수 이름을 사용하는 방법이다.
- 서술적인 변수 이름은 많을수록 더 좋다.
- If/Else 혹은 Switch/Case 문보다 다형성을 사용하라
- 대다수 개발자가
switch
문을 사용하는 이유는 손쉬운 선택이기 때문이다.switch
문을 사용하기 전에 다형성을 먼저 고려하라.
- 유형보다 함수가 더 쉽게 변하는 경우는 극히 드물다.
- 선택 유형 하나에는
switch
문을 한 번만 사용하고 같은 선택을 수행하는 다른 코드에는 다형성 객체를 사용하라.
- 선택 유형 하나에는
- 대다수 개발자가
- 조건을 캡슐화하라
- bool 논리는 이해하기 어렵다.
- 조건의 의도를 분명히 밝히는 함수로 표현하라
- 예시
라는 코드는 다음 코드보다 좋다.if (shouldBeDeleted(timer))
if (timer.hasExpired() && !timer.isRecurrent())
- 부정 조건은 피하라
- 부정 조건은 긍정 조건보다 이해하기 어렵다.
- 가능하면 긍정 조건으로 표현한다.
- 함수는 한 가지만 해야 한다.
- 한 가지만 수행하는 좀 더 작은 함수 여럿으로 나누는 것이 마땅하다.
- 예시
위의 함수는 다음과 같이 함수 셋으로 나누는 편이 좋다.public void pay() { for (Employee e : employees) { if (e.isPayday()) { Money pay = e.calculatePay(); e.deliveryPay(pay); } } }
위에서 각 함수는 한 가지 일만 수행한다.public void pay() { for (Employee e : employees) { payIfNeessary(e); } } public void payIfNecessary(Employee e) { if (e.isPayday()) { cacluateAndDeliverPay(e); } } private void calculateAndDeliveryPay(Employee e) { Money pay = e.calculatePay(); e.deliverPay(pay); }
- 숨겨진 시간적인 결합
- 때로는 시간적인 결합이 필요하지만 이를 숨겨서는 안 된다.
- 함수를 작성할 때 함수 인수를 적절히 배치해 함수가 호출되는 순서를 노출시켜라.
- 예시
public class MoogDriver { public void dive(String reason) { Gradient gradient = staturateGradient(); List<Spline> splines = reticulateSplines(gradient); diveForMoog(splines, reason); } }
- 추이적 탐색을 피하라
- 일반적으로 한 모듈은 주변 모듈을 모를수록 좋다.
- A가 B를 사용하고 B가 C를 사용하더라도 A가 C를 알 필요는 없다는 뜻이다.
- 이를 디미터의 법칙이라 부르는데 가장 중요한 것은 자신이 직접 사용하는 모듈만 알아야 한다는 뜻이다.
- 한 소스 파일에 여러 언어를 사용한다.
- 자바
- 긴 import 목록을 피하고 와일드카드를 사용하라
- 패키지에서 클래스를 둘 이상 사용한다면 와일드카드(*)를 사용해 패키지 전체를 가져온다.
- 예시
import package.*;
- 예시
- 긴 import 목록은 읽기에 부담스럽다.
- 사용하는 패키지를 간단히 명시하면 충분하다.
- 패키지에서 클래스를 둘 이상 사용한다면 와일드카드(*)를 사용해 패키지 전체를 가져온다.
- 상수 대 Enum
- 자바 5부터
enum
을 지원하기에public static final int
라는 옛날 관습을 더 이상 사용할 필요가 없다. - 메서드와 필드도 사용할 수 있기에 유연하고 서술적인 강력한 도구다.
- 좋은 예시
public enum HourlyPayGrade { APPRENTICE { public double rate() { return 1.0; } }, LIEUTENANT_JOURNEYMAN { public double rate() { return 1.2; } }, JOURNEYMAN { public double rate() { return 1.5; } }, MASTER { public double rate() { return 2.0; } }; public abstract double rate(); }
- 자바 5부터
- 긴 import 목록을 피하고 와일드카드를 사용하라
- 테스트
-
불충분한 테스트
2. 🤔 이해가 어려운 부분
🔍 질문하기
책을 읽으며 이해하기 어려웠던 개념이나 명확하지 않았던 내용을 정리합니다.
- 개념 또는 원칙의 이름
- 어려웠던 부분
해당 개념이 헷갈리거나 명확하지 않았던 점을 구체적으로 설명합니다. - 궁금한 점
해당 개념이 어떤 원리로 동작하는지, 실무에서 어떻게 활용되는지 등을 질문 형태로 정리합니다.
- 어려웠던 부분
- 개념 또는 원칙의 이름
- 어려웠던 부분
. - 궁금한 점
.
- 어려웠던 부분
- 개념 또는 원칙의 이름
- 어려웠던 부분
. - 궁금한 점
.
- 어려웠던 부분
3. 📚 참고 사항
📢 논의하기
관련된 자료가 있다면 공유하고, 더 깊이 논의하고 싶은 아이디어나 의견을 정리합니다.
- 관련 자료 공유
- 추가 자료
관련 블로그 글이나 공식 문서 링크를 제공합니다.
- 추가 자료
- 논의하고 싶은 주제
- 주제
논의하고 싶은 내용을 간략히 정리합니다. - 설명
논의하고 싶은 이유를 작성합니다.
- 주제