From 2e62fba355380cff74532d304388469fa1fde3d5 Mon Sep 17 00:00:00 2001 From: Hee Sang Date: Tue, 8 Jul 2025 17:18:51 +0900 Subject: [PATCH 1/3] =?UTF-8?q?5=EC=9E=A5=20=EB=AC=B8=ED=9D=AC=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\353\254\270\355\235\254\354\203\201.md" | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 "5\354\236\245/\353\254\270\355\235\254\354\203\201.md" diff --git "a/5\354\236\245/\353\254\270\355\235\254\354\203\201.md" "b/5\354\236\245/\353\254\270\355\235\254\354\203\201.md" new file mode 100644 index 0000000..ebbc275 --- /dev/null +++ "b/5\354\236\245/\353\254\270\355\235\254\354\203\201.md" @@ -0,0 +1,75 @@ +# 5장 형식 맞추기 + +### 형식을 맞추는 목적 + +- 융통성 없이 맹목적으로 따르면 안 된다 +- 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다 +- 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다 + +### 적절한 행 길이를 유지하라 + +- Junit, FitNesse, Time and Money와 같은 시스템도 500줄을 넘어가는 파일이 없으며 대다수가 200줄 미만이다 +- 일반적으로 큰 파일보다 작은 파일이 이해하기 쉽다 + +**신문 기사처럼 작성하라** + +- 기사의 경우 첫 문단은 전체 기사 내용을 요약한다 +- 소스 파일도 이름은 간단하면서 설명이 가능하게 짓는다 +- 첫 부분은 고차원 개념과 알고리즘을 설명한다 +- 아래로 내려갈수록 의도를 세세하게 묘사한다 + +**개념은 빈 행으로 분리하라** + +- 각 행은 수식이나 절을 나타내고, 일련의 행 묶음은 완결된 생각 하나를 표현한다 +- 생각 사이는 빈 행을 넣어 분리해야 마땅하다 + +**세로 밀집도** + +- 줄바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다 +- 의미 없는 주석으로 연관성이 높은 코드를 떨어뜨려 놓지 말자 + +**수직 거리** + +- 정의된 코드를 찾으려 상속 관계를 줄줄이 거슬러 올라간 경험이 있는가 +- 시스템이 무엇을 하는지 이해하고 싶은데, 이를 찾느라 시간과 노력을 소모한다 +- 서로 밀접한 개념은 세로로 가까이 둬야 하며, 타당한 근거가 없다면 서로 밀접한 개념은 한 파일에 속해야 한다 +- **변수 선언** - 사용하는 위치에 최대한 가까이 선언한다 + - 우리가 만든 함수는 매우 짧으므로 지역 변수는 각 함수 맨 처음에 선언한다 +- **인스턴스 변수** - 클래스 맨 처음에 선언한다 + - 잘 알려진 위치에 인스턴스 변수를 모은다는 사실이 중요하다 +- **종속 함수** - 한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치한다 + - 첫째 함수에서 가장 먼저 호출하는 함수가 바로 아래에 오도록 정의하자 + - 이는 함수를 찾기 쉽게 하며, 모듈 전체의 가독성도 높인다 +- 상수를 알아야 마땅한 함수에서 실제로 사용하는 함수로 상수를 넘겨주는 방법이 좋다 +- **개념적 유사성** - 친화도가 높을수록 코드를 가까이 배치한다 + 1. 다른 함수를 호출해 생기는 직접적인 종속성 + 2. 변수와 그 변수를 사용하는 함수 + 3. 비슷한 동작을 수행하는 일군의 함수 + +### 가로 형식 맞추기 + +- 프로젝트 7개 조사 결과, 80자 이후부터 행 수는 급격하게 감소한다 +- 프로그래머는 명백하게 짧은 행을 선호한다 + +**가로 공백과 밀집도** + +- 공백을 넣으면 두 가지 주요 요소가 확실히 나뉜다 +- 함수 이름과 이어지는 괄호 사이에는 공백을 넣지 않는다. 서로 밀접하기 때문에 +- 괄호 안 인수는 공백으로 분리한다. 인수가 별개라는 사실을 보여주기 위해 +- b*b - 4*a*c + +**가로 정렬** + +- 가로 정렬의 경우 변수 유형은 무시하고 변수 이름부터 읽게 된다 +- 정렬이 필요할 정도로 목록이 길다면 문제는 목록 길이지 정렬 부족이 아니다 + +**들여쓰기** + +- 범위로 이뤄진 계층을 표현하기 위해 코드를 들여쓴다 +- 들여쓰기한 파일은 구조가 한눈에 들어온다 +- 빈 while 문이나 for 문의 경우 세미콜론(;)을 새 행에다 제대로 들여써서 넣어준다 + +### 팀 규칙 + +- 스타일은 일관적이고 매끄러워야 한다 +- 한 소스 파일에서 봤던 형식이 다른 소스 파일에도 쓰이리라는 신뢰감을 독자에게 줘야 한다 From da56017695ab8cec83194e31e01c9abb2f48a5e7 Mon Sep 17 00:00:00 2001 From: Hee Sang Date: Mon, 14 Jul 2025 10:00:27 +0900 Subject: [PATCH 2/3] =?UTF-8?q?6=EC=9E=A5=20=EB=AC=B8=ED=9D=AC=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\353\254\270\355\235\254\354\203\201.md" | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 "6\354\236\245/\353\254\270\355\235\254\354\203\201.md" diff --git "a/6\354\236\245/\353\254\270\355\235\254\354\203\201.md" "b/6\354\236\245/\353\254\270\355\235\254\354\203\201.md" new file mode 100644 index 0000000..c3c550f --- /dev/null +++ "b/6\354\236\245/\353\254\270\355\235\254\354\203\201.md" @@ -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와 같은 탐색 함수도 제공한다 +- 이곳엔 비즈니스 규칙을 추가하면 안된다 +- 자료 구조로 취급하며, 비즈니스 규칙은 객체를 따로 생성하여 작성한다 + +### 결론 + +- 객체는 동작을 공개하고 자료를 숨긴다 +- 그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다 From 0a7848bbc6799ee57e62137d1ba9b02eb4db06f7 Mon Sep 17 00:00:00 2001 From: Hee Sang Date: Mon, 14 Jul 2025 11:24:28 +0900 Subject: [PATCH 3/3] =?UTF-8?q?7=EC=9E=A5=20=EB=AC=B8=ED=9D=AC=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\353\254\270\355\235\254\354\203\201.md" | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 "7\354\236\245/\353\254\270\355\235\254\354\203\201.md" diff --git "a/7\354\236\245/\353\254\270\355\235\254\354\203\201.md" "b/7\354\236\245/\353\254\270\355\235\254\354\203\201.md" new file mode 100644 index 0000000..24b69b0 --- /dev/null +++ "b/7\354\236\245/\353\254\270\355\235\254\354\203\201.md" @@ -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을 넘기지 못하도록 금지하는 정책이 합리적이다