Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions 5장/문희상.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 5장 형식 맞추기

### 형식을 맞추는 목적

- 융통성 없이 맹목적으로 따르면 안 된다
- 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다
- 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다

### 적절한 행 길이를 유지하라

- Junit, FitNesse, Time and Money와 같은 시스템도 500줄을 넘어가는 파일이 없으며 대다수가 200줄 미만이다
- 일반적으로 큰 파일보다 작은 파일이 이해하기 쉽다

**신문 기사처럼 작성하라**

- 기사의 경우 첫 문단은 전체 기사 내용을 요약한다
- 소스 파일도 이름은 간단하면서 설명이 가능하게 짓는다
- 첫 부분은 고차원 개념과 알고리즘을 설명한다
- 아래로 내려갈수록 의도를 세세하게 묘사한다

**개념은 빈 행으로 분리하라**

- 각 행은 수식이나 절을 나타내고, 일련의 행 묶음은 완결된 생각 하나를 표현한다
- 생각 사이는 빈 행을 넣어 분리해야 마땅하다

**세로 밀집도**

- 줄바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다
- 의미 없는 주석으로 연관성이 높은 코드를 떨어뜨려 놓지 말자

**수직 거리**

- 정의된 코드를 찾으려 상속 관계를 줄줄이 거슬러 올라간 경험이 있는가
- 시스템이 무엇을 하는지 이해하고 싶은데, 이를 찾느라 시간과 노력을 소모한다
- 서로 밀접한 개념은 세로로 가까이 둬야 하며, 타당한 근거가 없다면 서로 밀접한 개념은 한 파일에 속해야 한다
- **변수 선언** - 사용하는 위치에 최대한 가까이 선언한다
- 우리가 만든 함수는 매우 짧으므로 지역 변수는 각 함수 맨 처음에 선언한다
- **인스턴스 변수** - 클래스 맨 처음에 선언한다
- 잘 알려진 위치에 인스턴스 변수를 모은다는 사실이 중요하다
- **종속 함수** - 한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치한다
- 첫째 함수에서 가장 먼저 호출하는 함수가 바로 아래에 오도록 정의하자
- 이는 함수를 찾기 쉽게 하며, 모듈 전체의 가독성도 높인다
- 상수를 알아야 마땅한 함수에서 실제로 사용하는 함수로 상수를 넘겨주는 방법이 좋다
- **개념적 유사성** - 친화도가 높을수록 코드를 가까이 배치한다
1. 다른 함수를 호출해 생기는 직접적인 종속성
2. 변수와 그 변수를 사용하는 함수
3. 비슷한 동작을 수행하는 일군의 함수

### 가로 형식 맞추기

- 프로젝트 7개 조사 결과, 80자 이후부터 행 수는 급격하게 감소한다
- 프로그래머는 명백하게 짧은 행을 선호한다

**가로 공백과 밀집도**

- 공백을 넣으면 두 가지 주요 요소가 확실히 나뉜다
- 함수 이름과 이어지는 괄호 사이에는 공백을 넣지 않는다. 서로 밀접하기 때문에
- 괄호 안 인수는 공백으로 분리한다. 인수가 별개라는 사실을 보여주기 위해
- b*b - 4*a*c

**가로 정렬**

- 가로 정렬의 경우 변수 유형은 무시하고 변수 이름부터 읽게 된다
- 정렬이 필요할 정도로 목록이 길다면 문제는 목록 길이지 정렬 부족이 아니다

**들여쓰기**

- 범위로 이뤄진 계층을 표현하기 위해 코드를 들여쓴다
- 들여쓰기한 파일은 구조가 한눈에 들어온다
- 빈 while 문이나 for 문의 경우 세미콜론(;)을 새 행에다 제대로 들여써서 넣어준다

### 팀 규칙

- 스타일은 일관적이고 매끄러워야 한다
- 한 소스 파일에서 봤던 형식이 다른 소스 파일에도 쓰이리라는 신뢰감을 독자에게 줘야 한다
63 changes: 63 additions & 0 deletions 6장/문희상.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 6장 객체와 자료 구조
- 변수를 비공개로 정의하는 이유는 남들이 변수에 의존하지 않게 만들고 싶어서다

### 자료 추상화

- 구현을 외부로 노출하지 말고 추상화를 통해 숨기자
- 하지만 단순히 인터페이스만 적용한다고 추상화가 이뤄지지 않는다
- 자료를 세세하게 공개하기보다는 추상적인 개념으로 표현하는 편이 좋다
- 개발자는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다

### 자료/객체 비대칭

- 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다
- 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다

### 디미터 법칙

- 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다
- 아래의 코드는 디미터 법칙을 어기는 듯이 보인다

```java
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
```

- 위와 같은 코드는 여러 객차가 한 줄로 이어진 기차처럼 보이기 때문에 조잡하다 여겨지므로 피하는 편이 좋다

```java
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
```

- 위처럼 개선할 수 있다
- 하지만 디미터 법칙을 위반하는지 여부는 ctxt, Options, ScratchDir이 객체인지 아니면 자료 구조인지에 달렸다
- 자료 구조라면 당연히 내부 구조를 노출하므로 디미터 법칙이 적용되지 않는다

```java
final String outputDir = ctxt.options.scratchDir.absolutePath;
```

- 자료 구조는 무조건 함수 없이 공개 변수만 포함하고 객체는 비공개 변수와 공개 함수를 포함

**구조체 감추기**

- 객체한테는 뭔가를 하라고 말해야지 속을 드러내라고 말하면 안 된다
- 위의 경우에는 절대 경로를 얻으려는 이유가 임시 파일을 생성하기 위한 목적이기에 그에 맞춰 추상화 수준을 설정하자

### 자료 전달 객체

- 자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스다
- 흔히DTO는 일련의 단계에서 가장 처음으로 사용하는 구조체다

**활성 레코드**

- DTO의 특수한 형태다
- 공개 변수가 있거나 비공개 변수에 조회/설정 함수가 있는 자료 구조지만, 대게 save나 find와 같은 탐색 함수도 제공한다
- 이곳엔 비즈니스 규칙을 추가하면 안된다
- 자료 구조로 취급하며, 비즈니스 규칙은 객체를 따로 생성하여 작성한다

### 결론

- 객체는 동작을 공개하고 자료를 숨긴다
- 그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다
75 changes: 75 additions & 0 deletions 7장/문희상.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 7장 오류 처리
- 상당수 코드 기반은 전적으로 오류 처리 코드에 좌우된다

### 오류 코드보다 예외를 사용하라

- 예외를 지원하지 않는 언어는 오류를 처리하고 보고하는 방법이 제한적이었다
- 오류 플래그를 설정하거나 호출자에게 오류 코드를 반환하는 방법이 전부였다
- 예외를 던진다면 호출자 코드가 더 깔끔해지며, 논리가 오류 처리 코드와 뒤섞이지 않는다

### Try-Catch-Finally 문부터 작성하라

- Try-Catch-Finally 문에서 try 블록에 들어가는 코드를 실행하면 어느 시점에서든 실행이 중단된 후 catch 블록으로 넘어갈 수 있다
- try 블록은 트랜잭션과 비슷하며, try 블록에서 무슨 일이 생기든지 catch 블록은 프로그램 상태를 일관성 있게 유지해야 한다
- 먼저 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장한다

### 미확인(Unchecked)예외를 사용하라

- Checked 예외의 경우 메서드를 사용하는 방식이 메서드 선언과 일치하지 않으면 아예 컴파일도 못했다
- 확인된 예외는 OCP를 위반한다
- 모듈과 관련된 코드가 전혀 바뀌지 않았더라도 모듈을 다시 빌드한 다음 배포해야 한다
- 결과적으로 최하위 단계에서 최상위 단계까지 연쇄적인 수정이 일어난다
- throws 경로에 위치하는 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화 깨진다

### 예외에 의미를 제공하라

- 예외를 던질 때는 전후 상황을 충분히 덧붙인다
- 실패한 코드의 의도를 파악하려면 호출 스택만으로 부족하다
- 로깅 기능을 사용한다면 catch 블록에서 오류를 기록하도록 충분한 정보를 넘겨준다

### 호출자를 고려해 예외 클래스를 정의하라

- 프로그래머에게 가장 중요한 관심사는 오류를 잡아내는 방법이 되어야 한다
- 외부 API를 사용할 때는 감싸기 기법이 최선이다
1. 프로그램 사이에서 의존성이 크게 줄어든다
2. 나중에 다른 라이브러리로 갈아타도 비용이 적다
3. Wrapper 클래스에서 외부 API를 호출하는 대신 테스트 코드를 넣어주는 방법으로 프로그램을 테스트하기도 쉬워진다
4. 특정 업체가 API를 설계한 방식에 발목 잡히지 않는다

### 정상 흐름을 정의하라

```java
try {
MealExpenses expenses = expenseRepostDAO.getMeals(employee.getID()0;
m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
m_total += getMealPerDiem();
}
```

- 식비를 비용으로 청구하지 않았다면 일일 기본 식비를 총계에 더한다
- 하지만 예외가 논리를 따라가기 어렵게 만든다

```java
MealExpenses expenses = expenseRepostDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
```

- 이처럼 간결하게 만들어야 한다
- 이를 특수 사례 패턴(SPECIAL CASE PATTERN)이라 부른다
- 클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식이다

### null을 반환하지 마라

- null을 반환하는 코드는 일거리를 늘릴 뿐만 아니라 호출자에게 문제를 떠넘긴다
- null을 반환하고픈 유혹이 든다면 그 대신 예외를 던지거나 특수 사례 객체를 반환한다
- 사용하려는 외부 API가 null을 반환한다면 Wrapper 메서드를 구현해 예외를 던지거나 특수 사례 객체를 반환하는 방식을 고려한다
- List의 경우 값이 없다면 Collections.emptyList()를 통해 null 처리를 하지 않는 방향으로 구현하자

### null을 전달하지 마라

- 메서드에서 null을 반환하는 방식도 나쁘지만 메서드로 null을 전달하는 방식은 더 나쁘다
- 두 지점 사이의 거리를 계산하는 메서드에서 null을 전달하면 새로운 예외 유형을 만들어 던지는 방법이 있다
- assert문을 사용하는 방법도 있을 것이다
- 대다수의 프로그래밍 언어는 호출자가 실수로 넘기는 null을 적절히 처리하는 방법이 없다
- 따라서 애초에 null을 넘기지 못하도록 금지하는 정책이 합리적이다