코드의 품질을 매우 중요시했던 구글러들은 코드에 달린 주석에서 작은 마침표 오류까지 수정을 요청하기에 이르렀고, 그만큼 엄격했던 코드 품질 관리 덕분에 프로젝트의 유지 관리 비용이 매우 낮아졌다. 이와 같은 경험을 집대성한 이 책에서는 200여 개의 실제 프로젝트 사례를 바탕으로 객체지향 패러다임, 설계 원칙, 코딩 규칙, 리팩터링 기법, 디자인 패턴의 5가지 측면에서 고품질의 코드 작성 방법을 상세히 배울 수 있다.
Contents
옮긴이 머리말 xiii
베타리더 후기 xiv
시작하며 xvi
CHAPTER 1 개요 1
1.1 코드 설계를 배우는 이유 1
__1.1.1 고품질의 코드 작성 2 / 1.1.2 복잡한 코드 개발 다루기 2
__1.1.3 프로그래머의 기본 능력 4 / 1.1.4 경력 개발에 필요한 기술 5
__1.1.5 생각해보기 5
1.2 코드 품질 평가 방법 6
__1.2.1 유지 보수성 8 / 1.2.2 가독성 9 / 1.2.3 확장성 10 / 1.2.4 유연성 10
__1.2.5 간결성 11 / 1.2.6 재사용성 11 / 1.2.7 테스트 용이성 12 / 1.2.8 생각해보기 12
1.3 고품질 코드를 작성하는 방법 12
__1.3.1 객체지향 13 / 1.3.2 설계 원칙 13 / 1.3.3 디자인 패턴 14
__1.3.4 코딩 규칙 15 / 1.3.5 리팩터링 기법 15 / 1.3.6 생각해보기 17
1.4 과도한 설계를 피하는 방법 18
__1.4.1 코드 설계의 원래 의도는 코드 품질을 향상시키는 것이다 18
__1.4.2 코드 설계의 원칙은 앞에 문제가 있고, 뒤에 방안이 있다는 것이다 18
__1.4.3 코드 설계의 응용 시나리오는 복잡한 코드에 적용되어야 한다 19
__1.4.4 지속적인 리팩터링은 과도한 설계를 효과적으로 방지할 수 있다 20
__1.4.5 특정 시나리오 외의 코드 설계에 대해 이야기하지 않는다 20
__1.4.6 생각해보기 21
CHAPTER 2 객체지향 프로그래밍 패러다임 23
2.1 객체지향이란 무엇인가? 23
__2.1.1 객체지향 프로그래밍과 객체지향 프로그래밍 언어 23
__2.1.2 엄격하게 정의되지 않은 객체지향 프로그래밍 언어 25
__2.1.3 객체지향 분석과 객체지향 설계 26
__2.1.4 UML에 대한 참고 사항 27
__2.1.5 생각해보기 28
2.2 캡슐화, 추상화, 상속, 다형성이 등장한 이유 28
__2.2.1 캡슐화 28 / 2.2.2 추상화 31 / 2.2.3 상속 33 /
__2.2.4 다형성 35 / 2.2.5 생각해보기 39
2.3 객체지향 분석, 객체지향 설계, 객체지향 프로그래밍을 수행하는 방법 40
__2.3.1 예제 소개와 난이도 분석 40
__2.3.2 객체지향 분석 수행 방법 41
__2.3.3 객체지향 설계 방법 45
__2.3.4 객체지향 프로그래밍을 하는 방법 53
__2.3.5 생각해보기 55
2.4 객체지향 프로그래밍, 절차적 프로그래밍, 함수형 프로그래밍의 차이 55
__2.4.1 절차적 프로그래밍 55
__2.4.2 객체지향 프로그래밍과 절차적 프로그래밍의 비교 59
__2.4.3 함수형 프로그래밍 62
__2.4.4 객체지향 프로그래밍과 함수형 프로그래밍의 비교 69
__2.4.5 생각해보기 69
2.5 객체지향 프로그래밍처럼 보이지만 실제로는 절차적 프로그래밍 70
__2.5.1 getter, setter 메서드 남용 70
__2.5.2 전역 변수와 전역 메서드의 남용 74
__2.5.3 데이터와 메서드 분리로 클래스 정의하기 77
__2.5.4 생각해보기 79
2.6 빈약한 도메인 모델에 기반한 전통적인 개발 방식은 OOP를 위반하는가? 79
__2.6.1 빈약한 도메인 모델에 기반한 전통적인 개발 방식 80
__2.6.2 풍성한 도메인 모델에 기반한 DDD 개발 방식 82
__2.6.3 두 가지 개발 방식의 비교 83
__2.6.4 빈약한 도메인 모델에 기반한 전통적인 개발 방식이 널리 사용되는 이유 90
__2.6.5 풍성한 도메인 모델에 기반한 DDD 개발 방식의 응용 시나리오 91
__2.6.6 생각해보기 92
2.7 추상 클래스와 인터페이스 93
__2.7.1 추상 클래스와 인터페이스의 정의와 차이점 93
__2.7.2 추상 클래스와 인터페이스의 의미 97
__2.7.3 추상 클래스와 인터페이스의 모의 구현 100
__2.7.4 추상 클래스와 인터페이스의 응용 시나리오 102
__2.7.5 생각해보기 102
2.8 인터페이스 기반 프로그래밍:
모든 클래스에 대해 인터페이스를 정의해야 할까? 102
__2.8.1 인터페이스를 이해하는 다양한 방법 103
__2.8.2 설계 철학을 실제로 적용해보자 104
__2.8.3 인터페이스의 남용을 방지하려면 어떻게 해야 할까? 108
__2.8.4 생각해보기 109
2.9 상속보다 합성 109
__2.9.1 상속이 더 이상 사용되지 않는 이유 109
__2.9.2 합성이 상속에 비해 나은 장점 112
__2.9.3 합성을 사용할지 상속을 사용할지 결정하기 114
__2.9.4 생각해보기 115
CHAPTER 3 설계 원칙 117
3.1 단일 책임 원칙 117
__3.1.1 단일 책임 원칙의 정의 및 해석 117
__3.1.2 클래스에 단일 책임이 있는지 판단하는 방법 118
__3.1.3 클래스의 책임이 가능한 한 자세하게 설명되어 있는지 여부 121
__3.1.4 생각해보기 123
3.2 개방 폐쇄 원칙 123
__3.2.1 확장할 때는 개방, 수정할 때는 폐쇄 123
__3.2.2 코드를 수정하는 것은 개방 폐쇄 원칙을 위반하는 것일까? 129
__3.2.3 확장할 때는 개방, 수정할 때는 폐쇄를 달성하는 방법 131
__3.2.4 프로젝트에 개방 폐쇄 원칙을 유연하게 적용하는 방법 133
__3.2.5 생각해보기 134
3.3 리스코프 치환 원칙 134
__3.3.1 리스코프 치환 원칙의 정의 134
__3.3.2 리스코프 치환 원칙과 다형성의 차이점 136
__3.3.3 리스코프 치환 원칙을 위반하는 안티 패턴 137
__3.3.4 생각해보기 139
3.4 인터페이스 분리 원칙 139
__3.4.1 API나 기능의 집합으로서의 인터페이스 139
__3.4.2 단일 API나 기능으로서의 인터페이스 141
__3.4.3 객체지향 프로그래밍에서의 인터페이스 142
__3.4.4 생각해보기 149
3.5 의존 역전 원칙 149
__3.5.1 제어 반전 150 / 3.5.2 의존성 주입 152 / 3.5.3 의존성 주입 프레임워크 153
__3.5.4 의존 역전 원칙 154 / 3.5.5 생각해보기 155
3.6 KISS 원칙과 YAGNI 원칙 155
__3.6.1 KISS 원칙의 정의와 해석 155
__3.6.2 적은 줄 수의 코드가 더 간단하지 않다 156
__3.6.3 복잡한 코드가 반드시 KISS 원칙을 위반하는 것은 아니다 158
__3.6.4 KISS 원칙을 만족하는 코드 작성 방법 160
__3.6.5 YAGNI 원칙과 KISS 원칙의 차이 160
__3.6.6 생각해보기 161
3.7 DRY 원칙 161
__3.7.1 코드 논리의 중복 161 / 3.7.2 기능적(의미론적) 중복 164
__3.7.3 코드 실행의 중복 165 / 3.7.4 코드 재사용성 167
__3.7.5 생각해보기 169
3.8 LoD 169
__3.8.1 높은 응집도와 낮은 결합도에 대한 생각 169
__3.8.2 LoD의 정의 171
__3.8.3 정의 해석 및 첫 번째 예제 코드 171
__3.8.4 정의 해석 및 두 번째 예제 코드 174
__3.8.5 생각해보기 177
CHAPTER 4 코딩 규칙 179
4.1 명명과 주석 179
__4.1.1 긴 이름과 짧은 이름 179
__4.1.2 문맥 정보를 사용한 명명 단순화 180
__4.1.3 비즈니스 용어집을 사용한 명명 통일 180
__4.1.4 명명은 정확하지만 추상적이어야 한다 181
__4.1.5 주석에 반드시 포함되어야 하는 것들 181
__4.1.6 주석이 많다고 좋은 것은 아니다 183
__4.1.7 생각해보기 183
4.2 코드 스타일 184
__4.2.1 클래스, 함수의 적절한 크기 184
__4.2.2 한 줄의 적절한 길이 185
__4.2.3 빈 줄을 활용한 코드 블록 구분 185
__4.2.4 4칸 들여쓰기 혹은 2칸 들여쓰기 185
__4.2.5 여는 중괄호는 어디에 놓여야 할까 186
__4.2.6 클래스의 멤버 순서 186
__4.2.7 생각해보기 187
4.3 코딩 팁 187
__4.3.1 복잡한 코드의 모듈화 187 / 4.3.2 함수의 매개변수 관리 188
__4.3.3 함수의 플래그 매개변수 제거 189 / 4.3.4 깊은 중첩 코드 제거 191
__4.3.5 설명 변수 194 / 4.3.6 생각해보기 195
CHAPTER 5 리팩터링 기법 197
5.1 리팩터링의 네 가지 요소: 목적, 대상, 시기, 방법 197
__5.1.1 리팩터링의 목적 197 / 5.1.2 리팩터링의 대상 199
__5.1.3 리팩터링의 시기 199 / 5.1.4 리팩터링의 방법 200
__5.1.5 생각해보기 201
5.2 단위 테스트 201
__5.2.1 단위 테스트에 대해 201
__5.2.2 단위 테스트 코드를 작성하는 이유 204
__5.2.3 단위 테스트를 설계하는 방법 206
__5.2.4 단위 테스트를 작성하기 어려운 이유 209
__5.2.5 생각해보기 210
5.3 코드 테스트 용이성 210
__5.3.1 테스트 가능한 코드를 작성하는 방법 210
__5.3.2 테스트가 불가능한 코드 220
__5.3.3 생각해보기 222
5.4 디커플링 223
__5.4.1 디커플링이 중요한 이유 223
__5.4.2 코드를 디커플링해야 하는지 판단하기 223
__5.4.3 코드 디커플링 방법 224
__5.4.4 생각해보기 227
5.5 리팩터링 예제 227
__5.5.1 ID 생성기의 요구 사항과 개발 배경 228
__5.5.2 사용 가능한 수준의 코드 구현 228
__5.5.3 코드 품질 문제를 찾는 방법 230
__5.5.4 가독성 향상을 위한 리팩터링 232
__5.5.5 코드 테스트 용이성 향상을 위한 리팩터링 234
__5.5.6 단위 테스트 코드 작성을 위한 리팩터링 236
__5.5.7 예외 처리를 위한 리팩터링 239
__5.5.8 생각해보기 251
CHAPTER 6 생성 디자인 패턴 253
6.1 싱글턴 패턴 (1) 253
__6.1.1 싱글턴 패턴의 정의 253 / 6.1.2 싱글턴 패턴의 구현 254
__6.1.3 싱글턴 패턴의 적용 259 / 6.1.4 싱글턴 패턴의 단점 263
__6.1.5 싱글턴 패턴의 대안 266 / 6.1.6 생각해보기 268
6.2 싱글턴 패턴 (2) 268
__6.2.1 싱글턴 패턴의 유일성 268
__6.2.2 스레드 전용 싱글턴 패턴 269
__6.2.3 클러스터 환경에서의 싱글턴 패턴 270
__6.2.4 다중 인스턴스 패턴 272
__6.2.5 생각해보기 273
6.3 팩터리 패턴 (1) 273
__6.3.1 단순 팩터리 패턴 274 / 6.3.2 팩터리 메서드 패턴 278
__6.3.3 추상 팩터리 패턴 281 / 6.3.4 팩터리 패턴의 적용 대상 283
__6.3.5 생각해보기 283
6.4 팩터리 패턴 (2) 284
__6.4.1 DI 컨테이너와 팩터리 패턴의 차이 284
__6.4.2 DI 컨테이너의 핵심 기능 284
__6.4.3 DI 컨테이너의 설계와 구현 287
__6.4.4 생각해보기 292
6.5 빌더 패턴 293
__6.5.1 생성자를 사용한 객체 생성 293
__6.5.2 setter 메서드를 사용한 멤버 변수 설정 295
__6.5.3 빌더 패턴을 이용한 매개변수 검증 296
__6.5.4 Guava에서 빌더 패턴 적용 299
__6.5.5 빌더 패턴과 팩터리 패턴의 차이 301
__6.5.6 생각해보기 301
6.6 프로토타입 패턴 302
__6.6.1 프로토타입 패턴의 정의 302
__6.6.2 프로토타입 패턴의 적용 302
__6.6.3 프로토타입 패턴의 구현 306
__6.6.4 생각해보기 310
CHAPTER 7 구조 디자인 패턴 313
7.1 프록시 패턴 313
__7.1.1 인터페이스 기반의 프록시 패턴 313
__7.1.2 상속 기반의 프록시 패턴 316
__7.1.3 리플렉션 기반의 동적 프록시 317
__7.1.4 프록시 패턴의 활용 방법 318
__7.1.5 생각해보기 320
7.2 데커레이터 패턴: Java IO 라이브러리의 기본 설계 사상 분석 320
__7.2.1 Java IO 라이브러리의 특이한 사용 방법 320
__7.2.2 상속 기반 설계 322
__7.2.3 데커레이터 패턴 기반 설계 계획 323
__7.2.4 생각해보기 328
7.3 어댑터 패턴 328
__7.3.1 클래스 어댑터와 객체 어댑터 328
__7.3.2 어댑터 패턴의 응용 330
__7.3.3 자바 로깅과 어댑터 패턴 336
__7.3.4 래퍼 패턴 338
__7.3.5 생각해보기 342
7.4 브리지 패턴 343
__7.4.1 브리지 패턴의 정의 343
__7.4.2 브리지 패턴으로 폭발적인 상속 해결하기 343
__7.4.3 생각해보기 344
7.5 퍼사드 패턴 344
__7.5.1 퍼사드 패턴과 인터페이스 설계 345
__7.5.2 퍼사드 패턴의 응용: 인터페이스 사용성 개선하기 346
__7.5.3 퍼사드 패턴의 응용: 인터페이스 성능 향상하기 346
__7.5.4 퍼사드 패턴의 응용: 트랜잭션 문제 해결하기 346
__7.5.5 생각해보기 348
7.6 복합체 패턴 348
__7.6.1 복합체 패턴 기반의 디렉터리 트리 348
__7.6.2 복합체 패턴 기반의 휴먼 트리 353
__7.6.3 생각해보기 356
7.7 플라이웨이트 패턴 356
__7.7.1 체스 게임에서 플라이웨이트 패턴 적용 356
__7.7.2 텍스트 편집기에서 플라이웨이트 패턴 적용 359
__7.7.3 Java의 Integer에서 플라이웨이트 패턴 적용 362
__7.7.4 Java의 String에서 플라이웨이트 패턴 적용 367
__7.7.5 플라이웨이트 패턴과 싱글턴 패턴, 캐시, 오브젝트 풀의 차이 368
__7.7.6 생각해보기 369
CHAPTER 8 행동 디자인 패턴 371
8.1 옵서버 패턴 371
__8.1.1 옵서버 패턴의 정의 371
__8.1.2 옵서버 패턴의 코드 구현 372
__8.1.3 옵서버 패턴의 의미 373
__8.1.4 옵서버 패턴의 적용 376
__8.1.5 비동기식 비차단 옵서버 패턴 377
__8.1.6 EventBus 프레임워크 379
__8.1.7 EventBus 프레임워크를 처음부터 구현하기 382
__8.1.8 생각해보기 388
8.2 템플릿 메서드 패턴 (1) 388
__8.2.1 템플릿 메서드 패턴의 정의와 구현 388
__8.2.2 템플릿 메서드 패턴의 역할: 재사용 390
__8.2.3 템플릿 메서드 패턴의 역할: 확장 392
__8.2.4 생각해보기 395
8.3 템플릿 메서드 패턴 (2) 396
__8.3.1 콜백의 원리와 구현 396
__8.3.2 JdbcTemplate 클래스 398
__8.3.3 setClickListener() 메서드 401
__8.3.4 addShutdownHook() 메서드 402
__8.3.5 템플릿 메서드 패턴과 콜백의 차이점 404
__8.3.6 생각해보기 405
8.4 전략 패턴 405
__8.4.1 전략 패턴의 정의와 구현 405
__8.4.2 전략 패턴으로 분기 결정 대체 408
__8.4.3 전략 패턴을 통한 파일 내용 정렬 410
__8.4.4 전략 패턴의 오용 417
__8.4.5 생각해보기 417
8.5 책임 연쇄 패턴 417
__8.5.1 책임 연쇄 패턴의 정의와 구현 417
__8.5.2 책임 연쇄 패턴 기반의 민감 단어 필터링 423
__8.5.3 책임 연쇄 패턴 기반의 서블릿 필터 426
__8.5.4 책임 연쇄 패턴과 Spring의 인터셉터 430
__8.5.5 책임 연쇄 패턴과 MyBatis 플러그인 432
__8.5.6 생각해보기 439
8.6 상태 패턴 439
__8.6.1 유한 상태 기계란 무엇인가 439
__8.6.2 분기 판단 방법으로 상태 머신 구현하기 442
__8.6.3 테이블 조회 방법으로 상태 머신 구현하기 443
__8.6.4 상태 패턴으로 상태 머신 구현하기 446
__8.6.5 생각해보기 451
8.7 반복자 패턴 (1) 451
__8.7.1 반복자 패턴의 정의와 구현 451
__8.7.2 컬렉션 순회 방법 454
__8.7.3 반복자의 문제 456
__8.7.4 반복자의 문제 해결 458
__8.7.5 생각해보기 463
8.8 반복자 패턴 (2) 464
__8.8.1 스냅숏 기능을 지원하는 반복자 464
__8.8.2 여러 복사본 기반의 설계 사상 466
__8.8.3 시간값 기반의 설계 사상 466
__8.8.4 생각해보기 470
8.9 비지터 패턴 470
__8.9.1 비지터 패턴의 도출 과정 470
__8.9.2 이중 디스패치 481
__8.9.3 생각해보기 484
8.10 메멘토 패턴 485
__8.10.1 메멘토 패턴의 정의 및 구현 485
__8.10.2 시간과 공간 최적화 489
__8.10.3 생각해보기 490
8.11 커맨드 패턴 490
__8.11.1 커맨드 패턴의 정의 490
__8.11.2 모바일 게임 서버에 커맨드 패턴 적용 491
__8.11.3 커맨드 패턴과 전략 패턴의 차이 494
__8.11.4 생각해보기 494
8.12 인터프리터 패턴 494
__8.12.1 인터프리터 패턴의 정의 494
__8.12.2 인터프리터 패턴으로 표현식 계산하기 495
__8.12.3 인터프리터 패턴으로 규칙 엔진 개발하기 499
__8.12.4 생각해보기 502
8.13 중재자 패턴 502
__8.13.1 중재자 패턴의 정의와 구현 503
__8.13.2 중재자 패턴과 옵서버 패턴의 차이점 504
__8.13.3 생각해보기 505
찾아보기 506
Author
왕정,김진호
구글에서 번역 관련 시스템 개발에 참여했고 10여 년간 알고리즘을 연구해왔다. 현재는 금융회사에서 수석 시스템 아키텍트로서 회사의 비즈니스 아키텍처 설계 및 개발을 책임지고 있다.
구글에서 번역 관련 시스템 개발에 참여했고 10여 년간 알고리즘을 연구해왔다. 현재는 금융회사에서 수석 시스템 아키텍트로서 회사의 비즈니스 아키텍처 설계 및 개발을 책임지고 있다.