diff --git a/Chapter10/mangcho.md b/Chapter10/mangcho.md new file mode 100644 index 0000000..6160638 --- /dev/null +++ b/Chapter10/mangcho.md @@ -0,0 +1,128 @@ +# 좋은 코드, 나쁜 코드 (10) 단위 테스트 원칙 + +date: March 10, 2024 +preview: 좋은 코드, 나쁜 코드 10장을 읽고 정리한 글입니다. + +--- + +### 💭 좋은 단위 테스트는 어떻게 작성할 수 있는가 + +좋은 단위 테스트가 가져야 할 기능은 다음과 같다. + +- 훼손의 정확한 감지 +- 세부 구현 사항에 독립적 +- 잘 설명되는 실패 +- 이해할 수 있는 테스트 코드 +- 쉽고 빠르게 실행 + +**훼손의 정확한 감지** + +초기에 작성된 코드가 코드베이스에 병합되기 전에 테스트를 통해 실수를 발견할 수 있다. 또한, 코드 변경으로 인해 잘 돌아가던 기능이 동작하지 못하는 경우를 회귀라고 하는데 회귀를 탐지할 목적으로 테스트를 실행할 수 있다. + +**세부 구현 사항에 독립적** + +코드베이스가 생기는 변화는 기능적 변화와 리팩터링이다. 기능적 변화가 생기면 테스트도 충분히 수정할 가능성이 높다. 리팩터링은 코드의 구조만 변경되는 것이기에 테스트가 유지되어야 한다. 하지만 테스트가 깨지는 경우는 실수로 코드의 동작을 변경했다는 의미다. + +**잘 설명되는 실패** + +코드의 변경으로 테스트가 실패한다면, 문제 상황을 쉽게 파악할 수 있어야 한다. 제대로 알려주지 않는다면 그것을 알아내기 위해 많은 시간을 낭비해야 한다. + +**이해할 수 있는 테스트 코드** + +너무 많은 것을 테스트하거나 너무 많은 공유 테스트 설정을 하는 경우에 이해하기 어려울 수 있다. 테스트 코드를 이해하기 쉽게 만들어야 하는 이유는 일부 개발자들은 사용설명서로 사용하기 때문이다. + +**쉽고 빠르게 실행** + +단위 테스트는 매우 자주 실행된다. 테스트에 오랜 시간이 걸리면 테스트 자체가 힘든 작업이 돼버린다. + +### 💭 퍼블릭 API에 집중하되 중요한 동작은 무시하지 말라 + +개발자 사이에서 종종 언급되는 말이 있다. 다음은 훌륭한 조언이지만 절대적이지 않다. + +- Public API만을 이용해 테스트하라 +- 실행 세부 사항을 테스트하지 말라 + +하지만 추상화 계층 관점에서는 한 코드가 다른 코드에 대해 알아야 할 모든 것이 퍼블릭 API로 제공되기 때문에 그 외의 모든 것은 구현 세부사항이라고 볼 수 있다. 그러나 테스트에 관해서는 퍼블릭 API로 제공되지 않는 것 중에서도 테스트 코드가 알아야 할 다른 사항들이 있을 수 있다. 예를 들어, 다음과 같은 상황이 존재한다. + +- 서버와 상호작용하는 코드 +- 데이터베이스에 값을 저장하거나 읽는 코드 + +### 💭 테스트 더블 + +다른 코드에 의존성이 있는 코드를 테스트하는 방법 중 하나는 테스트 더블이다. 테스트 더블은 의존성을 시뮬레이션하는 객체지만 테스트에 더 적합하게 사용할 수 있게 만든다. 목, 스텁, 페이크 등이 있는데 페이크를 사용하는 것이 낫다. + +테스트 더블을 사용하는 이유는 다음과 같다. + +- 테스트 단순화 + + 실제 의존성을 설정하거나 하위 종속성에서 무언가를 검증할 필요가 없다. 더 빠르게 테스트를 실행할 수 있다. + +- 테스트로부터 외부 세계 보호 + + 테스트가 실제 데이터베이스에 의존하는 경우에 데이터가 더러워지는 경우가 예시다. + +- 외부로부터 테스트 보호 + + 반대로 실제 데이터베이스에 의존하는 경우에 데이터의 변경으로 테스트가 깨지는 경우가 예시다. + + +**목** + +클래스나 인터페이스를 시뮬레이션하는 데 멤버 함수에 대한 호출을 기록하는 것 외에는 어떠한 일도 하지 않는다. + + +**스텁** + +함수가 호출되면 미리 정해 놓은 값을 반환한다. 테스트 대상 코드가 의존하는 다른 코드로부터 값을 받아와야 하는 경우에 그 의존성을 시뮬레이션하는 데 유용하다. + + + +하지만 목과 스텁은 문제가 될 수 있다. + +- 목이나 스텁이 실제 의존성과 다른 방식으로 동작하도록 설정되면 테스트는 실제적이지 않다. +- 구현 세부 사항과 테스트가 밀접하게 결합하여 리팩터링이 어려워질 수 있다. + +**페이크** + +페이크는 실제 의존성의 공개 API를 정확하게 시뮬레이션하지만 구현은 단순하고 외부와 통신하는 대신에 페이크 내의 멤버 변수에 상태를 저장한다. 요점은 코드 계약이 실제 의존성과 동일하기 때문에 실제 클래스(또는 인터페이스)가 특정 입력을 받아들이지 않는다면 페이크도 마찬가지라는 점이다. + + + +페이크를 사용할 때의 장점은 다음과 같다. + +- 페이크로 인해 보다 실질적인 테스트가 이루어질 수 있다. +- 페이크를 사용하면 구현 세부 정보로부터 테스트를 분리할 수 있다. + +### 💭 테스트 철학으로부터 신중하게 선택하라 + +테스트를 둘러싼 여러 철학과 방법론이 존재한다. + +- **TDD (테스트 주도 개발)** + + 실제 코드를 작성하기 전에 테스트 케이스를 먼저 작성한다. 실제 코드는 테스트만 통과하도록 최소한만 작성하고 이후에 구조를 개선하고 중복을 없얘기 위한 리팩터링을 한다. + +- **BDD (행동 주도 개발)** + + 이 철학의 핵심은 사용자, 고객, 비즈니스 관점에서 소프트웨어가 보여야 할 행동을 식별하는 데에 집중하는 것이다. + +- **ATDD (수용 테스트 주도 개발)** + + 고객의 관점에서 소프트웨어가 보여줘야 하는 동작을 식별하고 소프트웨어가 전체적으로 필요에 따라 작동하는지 검증하기 위해 자동화된 수락 테스트를 만드는 것이다. TDD와 마찬가지로 실제 코드를 구현하기 전에 이러한 테스트를 생성해야 한다. 합격 테스트를 모두 통과하면 소프트웨어는 완전한 것이며 고객이 수락할 준비가 된 것이다. + + +### 💭 요약 + +- 코드 베이스에 제출된 거의 모든 '실제 코드'는 그에 해당하는 단위 테스트가 동반되어야 한다. +- '실제 코드'가 보여주는 모든 동작에 대해 이를 실행해보고 결과를 확인하는 테스트 케이스가 작성되어야 한다. 아주 간단한 테스트 케이스가 아니라면 각 테스트 케이스 코드는 준비, 실행 및 단언의 세 가지 부분으로 나누는 것이 일반적이다. +- 바람직한 단위 테스트의 주요 특징은 다음과 같다. + - 문제가 생긴 코드의 정확한 탐지 + - 구현 세부 정보에 구애받지 않음 + - 실패가 잘 설명됨 + - 이해하기 쉬운 테스트 코드 + - 쉽고 빠르게 실행 +- 테스트 더블은 의존성을 실제로 사용하는 것이 불가능하거나 현실적으로 어려울 때 단위테스트에 사용할 수 있다. 테스트 더블의 몇 가지 예는 다음과 같다. + - 목 + - 스텝 + - 페이크 +- 목 및 스텁을 사용한 테스트 코드는 비현실적이고 구현 세부 정보에 밀접하게 연결될 수 있다. +- 목과 스텁의 사용에 대한 여러 의견이 있다. 필자의 의견은 가능한 한 실제 의존성이 테스트에 사용되어야 한다는 것이다. 이렇게 할 수 없다면, 페이크가 차선책이고, 목과 스텁은 최후의 수단으로만 사용되어야 한다. \ No newline at end of file diff --git a/Chapter11/mangcho.md b/Chapter11/mangcho.md new file mode 100644 index 0000000..acab942 --- /dev/null +++ b/Chapter11/mangcho.md @@ -0,0 +1,87 @@ +# 좋은 코드, 나쁜 코드 (11) 단위 테스트의 실제 + +date: March 10, 2024 +preview: 좋은 코드, 나쁜 코드 11장을 읽고 정리한 글입니다. + +--- + +### 💭 기능뿐만 아니라 동작을 시험하라 + +단순히 눈에 보이는 대로 함수 이름을 테스트 목록에 넣기보다는 함수가 수행하는 모든 동작으로 목록을 채우는 것이 좋다. + +한 메서드는 다음과 같은 동작을 한다고 생각하자. + +- 다음 중 해당하는 사항이 있으면 대출에 기각된다. + - 신용 등급이 좋지 않다. + - 이미 대출이 있다. + - 대출이 금지된 고객이다. +- 주택담보대출 신청이 받아들여진다면 최대 대출 금액은 고객의 수입에서 지출을 뺀 금액에 10을 곱한 금액이다. + +함수 이름을 목록에 넣는다면 1개의 테스트가 만들어진다. 하지만 동작을 테스트로 만든다면 4개 이상의 테스트가 만들어진다. 모든 동작이 테스트되었는지 확인하기 위해 코드를 검토할 때, 사용할 수 있는 체크리스트가 있다. + +- 삭제해도 여전히 컴파일되거나 테스트가 통과되는 라인이 있는가 +- if문의 참 거짓 논리를 반대로 해도 테스트가 통과하는가 +- 논리 연산자나 산술 연산자를 다른 것으로 대체해도 테스트가 통과하는가 +- 상숫값이나 하드 코딩된 값을 변경해도 테스트가 통과하는가 + +### 💭 테스트만을 위해 퍼블릭으로 만들지 말라 + +프라이빗 함수는 구현 세부 사항이다. 테스트를 위해 퍼블릭 함수로 만드는 것의 문제점은 다음과 같다. + +- 이 테스트는 실제로 우리가 신경 쓰는 행동을 테스트하는 것이 아니다. 우리가 신경써야 할 것은 구현 세부 사항이 아니라 퍼블릭 API이다. +- 테스트가 구현 세부 사항에 독립적이지 못하게 된다. +- 다른 개발자가 퍼블릭으로 바꾼 함수에 의존할 수 있다. + +퍼블릭 API만을 사용해서 테스트하는 것이 좋다. 하지만 클래스가 더 복잡하거나 많은 논리를 포함하면 퍼블릭 API를 통해 모든 동작을 테스트하는 것이 까다로울 수 있다. 이 경우는 코드의 추상화 계층이 너무 크다는 것을 의미하기 때문에 코드를 더 작은 단위로 분할하는 것이 유익하다. + +### 💭 한 번에 하나의 동작만 테스트하라 + +여러 동작을 한꺼번에 테스트하면 테스트가 제대로 안 될 수 있다. 테스트 코드를 이해하기 어려운 단점도 존재한다. 이를 해결하는 방법은 각 동작을 자체 테스트 케이스에서 테스트하면 된다. 코드 중복이 많아진다는 단점도 존재하지만 이것은 매개변수화된 테스트로 방지할 수 있다. + +### 💭 공유 설정을 적절하게 사용하라 + +테스트 케이스에 의존성을 설정하거나 테스트 데이터를 만드는 것은 비용이 많이 들 수 있다. 이를 해결하는 방법은 다음 2가지가 있다. + +- **상태 공유** + + 모든 테스트 케이스 전에 한 번 실행된다. 설정된 모든 상태는 모든 테스트 케이스 간에 공유된다. Ex) BeforeAll + +- **설정 공유** + + 테스트 케이스는 이 코드에 의해 모든 설정을 공유한다. 각 테스트 케이스 전에 실행되므로 테스트 케이스 간에 공유되는 상태는 없다. Ex) BeforeEach + + + +비용을 줄이기 위해 상태와 설정을 공유하더라도 오히려 비용이 증가하는 경우가 발생한다. 적절하게 사용하는 것이 중요하다. 또한 상태와 설정을 공유하게 되면 초기화 등의 문제가 발생할 수 있으므로 조심해야 한다. + +### 💭 적절한 어서션 확인자를 사용하라 + +적절한 어서션은 테스트가 실패했을 경우에 이유를 잘 설명한다. + + +**적절한 어서션 확인자** + + + +### 💭 테스트 용이성을 위해 의존성 주입을 사용하라 + +데이터베이스와 같이 외부 의존성을 갖는 코드는 테스트하기 어렵다. 이는 의존성 주입을 통해 해결할 수 있다. + + +### 💭 테스트에 대한 몇 가지 결론 + +- 통합 테스트: 여러 구성 요소와 하위 시스템을 연결하는 프로세스를 통합이라고 한다. 이런 통합이 잘 이루어져 작동하는지 확인하기 위한 테스트 +- 종단 간 테스트: 소프트웨어의 처음부터 끝까지 통과하는 모든 여정을 테스트 + +- 회귀 테스트: 소프트웨어의 동작이나 기능이 바람직하지 않은 방식으로 변경되지 않았는지 확인하기 위해 정기적으로 수행하는 테스트 +- 골든 테스트: 특성화 테스트라고도 하며, 주어진 입력 집합에 대해 코드가 생성한 출력을 스냅샷으로 저장한 것을 기반으로 한다. 테스트 수행 후 코드가 생성한 출력이 다르면 테스트는 실패한다. +- 퍼즈 테스트: 많은 무작위 값이나 '흥미로운' 값으로 코드를 호출하고 그들 중 어느 것도 코드의 동작을 멈추지 않는지 점검 + +### 💭 요약 + +- 각 함수를 테스트하는 것에 집중하다 보면 테스트가 충분히 되지 못하기 쉽다. 보통은 모든 중요한 행동을 파악하고 각각의 테스트 케이스를 작성하는 것이 더 효과적이다. +- 결과적으로 중요한 동작을 테스트해야 한다. 프라이빗 함수를 테스트하는 것은 거의 대부분 결과적으로 중요한 사항을 테스트하는 것이 아니다. +- 한 번에 한 가지씩만 테스트하면 테스트 실패의 이유를 더 잘 알 수 있고 테스트 코드를 이해하기가 더 쉽다. +- 테스트 설정 공유는 양날의 검이 될 수 있다. 코드반복이나 비용이 큰 설정을 피할 수 있지만 부적절하게 사용할 경우 효과적이지 못하거나 신뢰할 수 없는 결과를 초래할 수 있다. +- 의존성 주입을 사용하면 코드의 테스트 용이성이 상당히 향상될 수 있다. +- 단위테스트는 개발자들이 가장 자주 다루는 테스트 수준이지만 이것만이 유일한 테스트는 아니다. 높은 품질의 소프트웨어를 작성하고 유지하려면 여러가지 테스트 기술을 함께 사용해야 할 때가 많다. \ No newline at end of file diff --git a/Chapter4/mangcho.md b/Chapter4/mangcho.md new file mode 100644 index 0000000..7908845 --- /dev/null +++ b/Chapter4/mangcho.md @@ -0,0 +1,78 @@ +# 좋은 코드, 나쁜 코드 (4) 오류 + +date: February 28, 2024 + +--- + +### 💭 복구 가능성 + +오류는 다음 두 가지로 분류된다. + +- 복구 가능한 오류 + - 네트워크 오류가 발생하면 연결을 재시도할 수 있다. + - 중요하지 않은 작업에서 오류가 발생한다면, 시스템을 계속해도 괜찮다. +- 복구할 수 없는 오류 + - 잘못된 입력 인수로 호출 + - 일부 필요한 상태를 사전에 초기화하지 않음 + +오류를 복구할 방법이 있다면, 개발자는 문제를 빨리 발견하고 해결하는 것이 좋다. 여기에서 **신속한 실패**와 요란한 **실패의 개념**이 나온다. + +### 💭 견고성 vs 실패 + +오류를 대처하는 방법은 다음 중 하나이다. + +- 더 높은 코드 계층이 처리하거나 전체 프로그램이 작동을 멈춘다. +- 오류를 처리하고 계속 진행한다. + +**신속하게 실패하라** + +가능한 한 문제의 발생 지점으로부터 가까운 곳에서 오류가 나타나게 하는 것이다. 신속한 실패의 장점은 다음과 같다. + +- 발생하자마자 바로 실패나 오류를 보여주지 않으면 문제가 발생할 때 디버그하기 어렵다. +- 코드가 제대로 작동하지 않거나 잠재적으로 문제를 일으킬 수 있다. + +**요란하게 실패하라** + +오류가 발생하는데도 아무도 모른다면 고칠 방법이 없다. 요란한 실패는 이를 대처하기 위한 수단이다. + +다음은 오류를 처리하는 좋지 않은 방법이다. + +- 기본값 반환 Ex) -1 +- 아무것도 하지 않음 + +### 💭 **오류 전달 방법** + +오류가 발생하면 일반적으로 더 높은 계층으로 오류를 알려야 한다. 전달하는 방법에는 크게 2가지가 있다. + +- 명시적 방법 + + 코드를 직접 호출한 쪽에서 오류가 발생할 수 있음을 인지할 수 밖에 없도록 한다. + +- 암시적 방법 + + 코드를 호출하는 쪽에 오류를 알리지만, 호출하는 쪽에서 그 오류를 신경쓰지 않아도 된다. + + + +자바는 검사 예외(checked exception)와 비검사 예외(unchecked exception)의 개념을 모두 가지고 있다. 예외를 지원하는 대부분의 주요 언어는 비검사 예외만 가지고 있으므로 자바 이외의 거의 모든 언어에서 예외라는 용어는 일반적으로 비검사 예외를 의미한다. + +**명시적 방법: 검사 예외 → checked exception** + +컴파일러는 검사 예외에 대해 호출하는 쪽에서 예외를 인지하도록 강제적으로 조치하는데, 호출하는 쪽에서는 예외 처리를 위한 코드를 작성하거나 자신의 함수 시그니처에 해당 예외 발생을 선언해야 한다. 따라서 검사 예외를 사용하는 것은 오류를 전달하기 위한 명시적인 방법이다. + +**암시적 방법: 비검사 예외 → unchecked exception** + +비검사 예외를 사용하면 다른 개발자들은 코드가 이 예외를 발생시킬 수 있다는 사실을 전혀 모를 수 있다. 따라서 비검사 예외는 오류가 발생할 수 있다는 것을 호출하는 쪽에서 인지하리라는 보장이 없기 때문에 오류를 암시적으로 알리는 방법이다. + +### 💭 요약 + +- 오류에는 크게 두 가지 종류가 있다. + - 시스템이 복구할 수 있는 오류 + - 시스템이 복구할 수 없는 오류 +- 해당 코드에 의해 생성된 오류로부터 복구할 수 있는지 여부를 해당 코드를 호출하는 쪽에서만 알 수 있는 경우가 많다. +- 에러가 발생하면 신속하게 실패하는 것이 좋고, 에러를 복구할 수 없는 경우에는 요란하게 실패하는 것이 바람직하다. +- 오류를 숨기는 것은 바람직하지 않을 때가 많으며, 오류가 발생했다는 신호를 보내는 것이 바람직하다. +- 오류 전달 기법은 두 가지 범주로 나눌 수 있다. + - 명시적 방법: 코드 계약의 명확한 부분. 호출하는 쪽에서는 오류가 발생할 수 있음을 인지한다. + - 암시적 방법: 코드 계약의 세부 조항을 통해 오류에 대한 설명이 제공되거나 전혀 설명이 없을 수도 있다. 오류가 발생할 수 있다는 것을 호출하는 쪽에서 반드시 인지하는 것은 아니다. +- 복구할 수 없는 오류에 대해서는 암시적 오류 전달 기법을 사용해야 한다. \ No newline at end of file diff --git a/Chapter5/mangcho.md b/Chapter5/mangcho.md new file mode 100644 index 0000000..7783e36 --- /dev/null +++ b/Chapter5/mangcho.md @@ -0,0 +1,101 @@ +# 좋은 코드, 나쁜 코드 (5) 가독성 높은 코드를 작성하라 + +date: February 28, 2024 +updatedAt: March 3, 2024 11:09 PM +tags: 📖 Review +status: Done +preview: 좋은 코드, 나쁜 코드 6장을 읽고 정리한 글입니다. + +--- + +### 💭 서술형 명칭 사용 + +서술적이지 않은 이름은 코드를 읽기 어렵게 만든다. 그리고, 주석문으로 서술적인 이름을 대체할 수 없다. + + +### 💭 주석문의 적절한 사용 + +주석문으로 달성할 수 있는 목적은 다음과 같다. + +- 코드가 **무엇**을 하는지 설명 +- 코드가 **왜** 그 일을 하는지 설명 +- 사용 지침 등 기타 정보 제공 + +하지만 대부분의 경우에 주석문은 지양하는 것이 좋다. + +- 중복된 주석문은 유해할 수 있다. 이미 코드로 잘 설명되는 작업을 굳이 주석문으로 작성할 필요 없다. +- 주석문으로 가독성 높은 코드를 대체할 수 없다. +- 주석문도 유지보수의 대상이다. +- 유지보수되지 않는 주석문은 없는 것보다 안 좋다. + +다음 경우에는 주석문을 사용하는 것이 유용할 수 있다. + +- 코드가 **왜** 그 일을 수행하는지는 코드 자체로 파악하기 어렵다. 존재 이유나 목적, 배경은 주석문을 통해 작성하는 것이 유용하다. +- 유용한 상위 수준의 요약 정보를 제공할 수 있다. + +### 💭 코드 줄 수를 고정하지 말라 + +일반적으로 코드 줄 수가 적으면 이해하기 쉬운 코드가 만들어 질 수 있다. 하지만 과도한 줄임은 역효과를 부른다. + +다음은 간결하지만 이해하기 어려운 코드이다. + + +이런 경우에, 더 많은 줄이 필요하더라도 가독성 높은 코드를 작성하는 것이 좋다. + + +잘 명명된 헬퍼 함수와 상수로 코드가 더 이해하기 쉽고, 하위 문제를 재사용할 수 있다는 장점이 생긴다. + +### 💭 일관된 코딩 스타일을 고수하라 + +자바에서 일반적으로 클래스 이름에 대해 파스칼 케이스(PascalCase)를, 변수 이름에는 캐멀 케이스(camelCase)를 사용한다. 뿐만 아니라 모든 코딩 스타일은 조직과 팀에 맞춰 따르면 된다. + +### 💭 깊이 중첩된 코드를 피하라 + +당연한 이야기다. 4중, 5중으로 중첩된 if문을 떠올려보면 답답하다. 중첩된 코드를 개선하는 방법은 다음과 같다. + +- 빠르게 return을 사용해서 중첩을 벗어난다. +- 중첩은 너무 많은 일을 한 결과물이다. 더 작은 함수로 분리해 보자. + +### 💭 함수 호출도 가독성이 있어야 한다 + + +위 코드는 1, true가 의미하는 바를 이해하기 어렵다. + + +메서드의 매개변수를 잘 명명한다고 해도 호출부에서 매개변수의 이름을 알 방법이 없다. + + +해결 방법 중 하나로 매개변수를 클래스나 열거형으로 분리할 수 있다. 하지만 때로는 해결책이 없을 수 있다. + +### 💭 설명되지 않은 값을 사용하지 말라 + +하드 코딩된 값에 대한 이야기로 예시는 다음과 같다. + +- 한 수량을 다른 수량으로 변환할 때 사용하는 계수 +- 작업이 실패할 경우 재시도의 최대 횟수와 같이 조정 가능한 파라미터 값 + +하드 코드된 값에 의미를 부여하는 방법은 다음과 같다. + +1. 상수로 분리한다. Ex) `KILOGRAMS_PER_US_TON = 907.1847` +2. 상수를 반환하는 공급자 함수를 사용한다. +3. 변환을 수행하는 헬퍼 함수를 사용한다. + + + +### 💭 익명 함수를 적절하게 사용하라 + +익명 함수는 간단한 로직에 좋다. 간단한 로직을 명명 함수로 분리하면 오히려 가독성이 떨어진다. + +복잡한 로직에 대해 익명 함수를 사용하면 가독성이 떨어질 수 있다. + +또한, 익명 함수가 길어지면 가독성이 떨어진다. 이때, 익명 함수를 여러 개의 명명 함수로 분리해 보자. + +### 💭 요약 + +- 코드의 가독성이 떨어져서 이해하기 어려울 때 다음과 같은 문제가 발생할 수 있다. + - 다른 개발자가 코드를 이해하느라 시간을 허비함 + - 버그를 유발하는 오해 + - 잘 작동하던 코드가 다른 개발자가 수정한 후에 작동하지 않음 +- 코드의 가독성을 높이다 보면 때로는 코드가 더 장황하게 되고 더 많은 줄을 작성해야 할 수도 있다. 이것은 종종 가치 있는 절충이다. +- 코드의 가독성을 높이려면 다른 개발자의 입장을 공감하고, 그들이 코드를 읽을 때 어떻게 혼란스러워할지를 상상해보는 것이 필요하다. +- 실제 시나리오는 다양하며 보통 그 상황에 해당하는 어려움이 있다. 가독성이 좋은 코드를 작성하려면 언제나 상식을 적용하고 판단력을 사용해야 한다. \ No newline at end of file diff --git a/Chapter6/mangcho.md b/Chapter6/mangcho.md new file mode 100644 index 0000000..4397724 --- /dev/null +++ b/Chapter6/mangcho.md @@ -0,0 +1,94 @@ +# 좋은 코드, 나쁜 코드 (6) 예측 가능한 코드를 작성하라 + +date: February 28, 2024 +updatedAt: March 3, 2024 11:35 PM +tags: 📖 Review +status: Done +preview: 좋은 코드, 나쁜 코드 6장을 읽고 정리한 글입니다. + +--- + +### 💭 매직값을 반환하지 말아야 한다 + +매직값의 일반적인 예는 -1이다. 어떤 메서드의 동작이 잘못된 경우에 -1을 반환하는 것을 볼 수 있다. + +해당 메서드를 호출하는 메서드는 잘못된 경우에 대처해야 한다. 이것은 예외 책임을 호출부에 전가하는 행위인데 매직값을 반환하는 것으로 예외가 발생할 수 있음을 표현하기에 충분하지 않다. 반환 타입이 int인 경우에 해당 메서드의 내부를 확인하지 않고 예외가 발생할 수 있다는 사실을 파악하기 어렵다. + +이를 해결하는 방법은 널, 옵셔널 또는 오류를 반환하는 것이다. 값이 없을 수 있는 경우 이것을 코드 계약을 통해 명백하게 표현하는 것이 좋다. 자바의 경우에 널 안정성을 지원하지 않기 때문에 옵셔널의 사용하는 것이 하나의 방법이다. + +이 방법에도 문제는 존재한다. 옵셔널로 널값이나 비어 있는 값이 발생한 이유를 설명하기 어렵다. + +### 💭 널 객체 패턴을 적절히 사용하라 + +널값이나 옵셔널을 대체할 방법으로 널 객체 패턴을 사용할 수 있다. 널 객체 패턴의 예시로 빈 문자열과 빈 리스트가 있고, 일반적으로 모든 멤버 메서드가 아무것도 하지 않는 기본 클래스를 구현한다. 이를 통해 얻는 장점은 다음과 같다. + +- 유효한 값이 반환되어 시스템 동작이 피해를 최소화한다는 것이다. +- 빈 컬렉션을 제공하는 방식으로 코드가 간결해 진다. 호출하는 메서드 내의 널 값이나 옵셔널을 처리하기 위한 코드를 제거할 수 있다. + +하지만, 단점도 명확하다. + +- 비즈니스 로직에 따라 반드시 존재해야 하는 값에 대해 널 객체는 올바르지 않을 수 있다. + + Ex) 반드시 필요한 카드 ID 정보가 빈 문자열로 반환되는 경우 + +- 더 복잡한 널 객체는 예측을 벗어날 수 있다. + + Ex) 머그컵이 존재하지 않는 경우, 널 객체가 크기가 0인 머그컵을 생성 + +- 널 객체의 구현이 예상을 벗어나는 경우가 있다. + + 널 객체 내부 메서드는 아무 동작을 하지 않기 때문에 예상과 다르게 동작할 수 있다. + +- 오류가 발생한 사실을 숨긴다는 이유로 경우에 따라 바람직하지 않을 수 있다. + +### 💭 예상치 못한 부수 효과를 피하라 + +### 💭 입력 매개변수를 수정하는 것에 주의하라 + +입력 매개변수를 수정하면 버그가 발생할 수 있다. 어떤 인스턴스가 여러 메서드를 오가는 상황은 빈번하다. 한 메서드가 매개변수로 전달되는 인스턴스 내부를 변경하는 경우에 예상치 못한 결과를 초래할 수 있다. + + + +이를 해결하는 방법은 복사본을 전달하는 것이다. + + +### 💭 오해를 일으키는 함수는 작성하지 말라 + +**중요한 매개변수가 누락된 경우에 예상하지 못한 방향으로 동작할 수 있다.** + + +`displayLegalDisclaimer`에 `legalText`에 널값이 전달되면 메서드는 아무런 동작을 하지 않는다. 메서드를 왜 이렇게 작성했을까? 호출하는 쪽에서 널에 대한 처리를 전가하지 않기 위함이다. 하지만 오해의 소지가 있고 예상을 벗어나는 동작을 하게 된다. + +이를 해결하는 방법은 중요한 입력을 필수 항목으로 두는 것이다. 어떤 매개변수 없이 메서드가 동작할 수 없는 경우에 해당 매개변수를 필수로 두고 널을 허용하지 않는 방법이 있다. + + +### 💭 미래를 대비한 열거형 처리 + +열거형에 대해 일부 개발자는 형 안전성을 제공하고 시스템에 유효하지 않은 입력을 방지할 수 있는 방법이라고 주장한다. 어떤 사람은 열거형이 코드 전반에 퍼져 간결한 추상화 계층을 막는다고 한다. 개발자마다 입장이 분분한데, 이에 대한 대비로 열거형을 사용하는 경우에 주의할 점을 알아보자. + +미래에 추가될 수 있는 열것값을 암묵적으로 처리하는 것은 문제가 될 수 있다. 다음 코드는 if-return을 통해 매개변수에 대한 반환값을 결정하고 있다. + + +만약 새로운 열거값이 추가된다면 어떻게 될까? + + +WORLD_WILL_END의 경우에도 true를 반환하는 대참사가 일어난다. 이를 해결하는 방법은 switch문을 사용하고 테스트를 작성하는 것이다. + + + +열거형에 새로운 값이 추가되면 테스트는 실패한다. 빠른 실패 덕분에 프로덕션 코드를 수정할 기회를 얻게 된다. 하지만, 이 방법이 완전한 것은 아니다. switch 문을 사용하는 경우에 기본 케이스를 주의해야 한다. + + +이 경우에는 default 부분에서 예외를 던지는 것으로 해결할 수 있다. + +이 밖에도 다른 프로젝트의 열거형에 의존하는 상황이 발생할 수 있는데, 이건 상황에 맞게 판단하여 대처하는 것이 필요하다. + +### 💭 요약 + +- 다른 개발자가 작성하는 코드는 종종 우리가 작성하는 코드에 의존한다. + - 다른 개발자가 우리 코드의 기능을 잘못 해석하거나 처리해야 하는 특수한 경우를 발견하지 못하면, 우리가 작성한 코드에 기반한 그 코드에서 버그가 발생할 가능성이 크다. + - 코드를 호출하는 쪽에서 예상한 대로 동작하기 위한 좋은 방법 중 하나는 중요한 세부사항이 코드 계약의 명백한 부분에 포함되도록 하는 것이다. +- 우리가 사용하는 코드에 대해 허술하게 가정을 하면 예상을 벗어나는 또 다른 결과를 볼 수 있다. + - 예를 들어 열거형에 추가되는 새 값을 예상하지 못한 경우다. + - 의존해서 사용 중인 코드가 가정을 벗어날 경우, 코드 컴파일을 중지하거나 테스트가 실패하도록 하는 것이 중요하다. + - 테스트만으로는 예측을 벗어나는 코드의 문제를 해결할 수 없다. 다른 개발자가 코드를 잘못 해석하면 테스트해야 할 시나리오도 잘못 이해할 수 있다. \ No newline at end of file diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c.md" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c.md" new file mode 100644 index 0000000..64a71bd --- /dev/null +++ "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c.md" @@ -0,0 +1,117 @@ +# 좋은 코드, 나쁜 코드 (7) 코드를 오용하기 어렵게 만들라 + +date: February 28, 2024 +updatedAt: March 3, 2024 11:10 PM +tags: 📖 Review +status: Done +preview: 좋은 코드, 나쁜 코드 7장을 읽고 정리한 글입니다. + +--- + +우리가 작성하는 코드는 훨씬 더 큰 소프트웨어의 일부일 뿐이다. 서로 다른 코드가 올바르게 작동하기 위해선 오용하기 어려운 코드를 작성해야 한다. 오용될 수 있는 몇 가지 사례는 다음과 같다. + +- 호출하는 쪽에서 잘못된 입력을 제공 +- 다른 코드의 부수 효과(입력 매개변수의 수정 등) +- 정확한 시간이나 순서에 따라 함수를 호출하지 않음 +- 관련 코드에서 가정과 맞지 않게 수정이 이루어짐 + +### 💭 불변 객체로 만드는 것을 고려하라 + +가변 객체를 지양해야 하는 근거는 다음과 같다. + +- 가변 객체는 추론하기 어렵다. 생성 시점과 현재 상태가 다를 수 있기 때문이다. +- 가변 객체는 다중 스레드에서 문제가 발생할 수 있다. 한 스레드가 해당 객체를 읽는 도중에 다른 스레드가 상태를 변경할 여지가 있다. +- 가변 객체를 전달 받는 모든 코드는 오용의 가능성이 생긴다. + +이를 해결하는 방법은 생성할 때만 값을 할당하여 불변 객체로 만드는 것이다. `final` 등의 키워드로 생성 이후에 객체의 상태가 변하지 않도록 한다. setter가 존재한다면 당연히 삭제한다. + +그럼, 객체의 상태에 수정이 필요한 경우는 어떻게 해야 할까? 불변성에 대한 디자인 패턴을 사용하는 것이 좋다. 예시로 빌더 패턴이나 복사 패턴이 있다. + +**빌더 패턴** + +클래스의 생성자로 모든 필드의 값을 세팅하는 데에는 비용이 발생한다. 일부 필드의 값이 선택 사항이라면 번거로운 일이다. 빌더 패턴은 한 클래스를 두 개로 나누는 효과를 갖는다. + +- 값을 하나씩 설정할 수 있는 빌더 클래스 +- 빌더에 의해 작성된 불변적인 읽기 전용 클래스 + +빌더 패턴을 통해 도메인 객체는 불변성을 유지하는 반면, 빌더 클래스는 생성자와 setter를 갖는 가변 객체이다. + +**쓰기 시 복사 패턴** + +setter와 비슷하지만, 객체의 상태를 변경하는 것이 아니라 변경된 상태를 갖는 인스턴스를 생성하여 반환한다. 다음은 `TextOptions` 클래스의 내부 메서드이다. + +![스크린샷 2024-03-03 오후 9.59.40.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_9.59.40.png) + +![스크린샷 2024-03-03 오후 10.00.05.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.00.05.png) + +### 💭 객체를 깊은 수준까지 불변적으로 만드는 것을 고려하라 + +다음 코드에서 `fontFamily` 변수는 `List` 인스턴스의 참조 값을 갖고 있다. `getFontFamily()`를 호출하면 이 참조 값을 반환한다. + +![스크린샷 2024-03-03 오후 10.01.46.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.01.46.png) + +위 클래스에서 `fontFamily`의 상태는 불변하다. 하지만 `fontFamily`가 참조하는 인스턴스의 내부는 가변적이다. 이를 해결하는 방법은 방어적 복사이다. + +![스크린샷 2024-03-03 오후 10.05.22.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.05.22.png) + +방어적 복사는 불변적인 클래스를 만들 수 있지만, 단점이 명확하다. + +- 복사하는 비용이 많이 들 수 있다. `fontFamily`에 수백개의 글꼴이 있다면 `getFontFamily()`가 호출될 때마다 많은 비용이 발생할 것이다. +- 깊은 불변성을 제공하지 못한다. 외부에 제공하는 경우에 복사하는 것이지, 내부적으로 변경가능하다. + +이를 해결하는 방법은 애초에 불변 자료구조를 사용하는 것이다. + +![스크린샷 2024-03-03 오후 10.09.09.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.09.09.png) + +### 💭 지나치게 일반적인 데이터 유형을 피하라 + +지구상의 좌표를 저장하는 자료 구조로 Double을 사용한다면 다음과 같을 것이다. + +![스크린샷 2024-03-03 오후 10.10.22.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.10.22.png) + +List와 Double은 너무 일반적인 자료구조이다. 이렇게 하면 코드를 오용하기 쉽고 이를 방지하기 위해 설명하기 시작하면, 그 코드는 가독성이 좋지 않은 코드의 증거다. + +- 위도와 경도의 순서를 바꿀 수 있다. +- 형식에 대한 안정성이 없다. 값이 존재하지 않을 수도 있다. + +주의해야 할 점은 패러다임은 퍼지기 쉽다는 점이다. + +개발 과정에서 좌표를 표현하기 위해 임시로 일반적인 자료구조를 사용했다고 가정하자. 이 패러다임은 이 좌표를 사용하는 다른 모든 클래스에게 전파된다. 모든 클래스는 좌표를 다루기 위해 일반적인 자료구조를 사용하고 문서화된다. 단지 하나의 자료구조로 인해 전체 코드베이스가 영향을 받게 되는 것이다. + +이를 해결하기 위한 방법으로 Pair를 떠올리기 쉽다. 하지만 Pair 역시 내부적으로 Interger, Double 같은 일반적인 자료구조를 사용하기에 오용하기 쉽다. + +가장 좋은 방법은 해당 도메인에 대한 객체를 생성하는 것이다. 이 객체를 통해 코드의 오용 가능성은 낮아지고, 더이상 추가적인 문서화가 필요 없어졌다. + +![스크린샷 2024-03-03 오후 10.16.00.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.16.00.png) + +### 💭 시간 처리 + +코드에서 시간을 처리하는 경우에 고려해야 할 점이 상당히 많다. + +- 표현하는 시간은 크게 2가지 종류가 있다. "어느 한 시점"과 "시간의 양" 이다. +- 시간의 단위는 다양하다. 밀리초, 초, 분, 시, 일 … +- 시간대 처리도 중요한 문제이다. + +이 문제를 해결하는 방법도 적절한 자료구조를 사용하는 것이다. 자바에서는 java.time 패키지를 제공한다. java.util 패키지도 시간과 관련한 클래스를 제공하지만 가변성으로 인해 사용하지 않도록 권고하고 있다. + +### 💭 데이터에 대해 진실의 원천을 하나만 가져야 한다 + +원천에 대해 다음 예시를 보면 쉽게 이해할 수 있다. + +![스크린샷 2024-03-03 오후 10.22.02.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.22.02.png) + +`balance`는 `credit-debit`이다. 하지만 생성자에서 이 공식과 다른 값이 입력될 수 있다. 이를 해결하는 방법은 데이터의 원천을 하나로 두는 것이다. 즉 `blance`는 제공하지 않고 `credit`과 `debit`으로 `blance`를 계산하는 것이다. + +하지만 이 방식이 문제가 되는 지점이 있는데, 계산 비용이 많이 드는 경우이다. 파생된 값을 계산하는 데에 많은 비용이 발생한다면, 그 계산을 지연(lazy)하는 것도 하나의 방법이다. 지연한다는 것의 의미는 값을 실제로 사용하는 시점에 연산을 진행한다는 의미이다. + +![스크린샷 2024-03-03 오후 10.25.38.png](%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3,%20%E1%84%82%E1%85%A1%E1%84%88%E1%85%B3%E1%86%AB%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%20(7)%20%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B3%E1%84%85%E1%85%B3%E1%86%AF%20%E1%84%8B%E1%85%A9%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5%20%E1%84%8B%E1%85%A5%E1%84%85%E1%85%A7%20f204d7f7a9574b49b712a8fcce33551c/%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA_2024-03-03_%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE_10.25.38.png) + +### 💭 요약 + +- 코드가 오용되기 쉽게 작성되고 나면 어느 시점에선가는 오용될 가능성이 크고 이것은 버그로 이어질 수 있다. +- 코드가 오용되는 몇 가지 일반적인 사례는 다음과 같다. + - 호출하는 쪽에서 잘못된 입력을 제공 + - 다른코드에서 일어나는 부수 효과 + - 함수 호출 시점이 잘못되거나 올바른 순서로 호출되지 않은 경우 + - 원래의 코드에 연관된 코드를 수정할 때 원래의 코드가 내포한 가정과 어긋나게 수정하는 경우 +- 오용이 어렵거나 불가능하도록 코드를 설계하고 구조화하는 것이 종종 가능하다. 이를 통해 버그 발생 가능성이 크게 줄어들고 중장기적으로 개발자의 시간을 많이 절약할 수 있다. \ No newline at end of file diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.00.05.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.00.05.png" new file mode 100644 index 0000000..4fe5f90 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.00.05.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.01.46.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.01.46.png" new file mode 100644 index 0000000..c4140b6 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.01.46.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.05.22.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.05.22.png" new file mode 100644 index 0000000..094f94c Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.05.22.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.09.09.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.09.09.png" new file mode 100644 index 0000000..5c5443c Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.09.09.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.10.22.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.10.22.png" new file mode 100644 index 0000000..a524a68 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.10.22.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.16.00.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.16.00.png" new file mode 100644 index 0000000..6c4b028 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.16.00.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.22.02.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.22.02.png" new file mode 100644 index 0000000..9368e94 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.22.02.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.25.38.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.25.38.png" new file mode 100644 index 0000000..2088a63 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.25.38.png" differ diff --git "a/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_9.59.40.png" "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_9.59.40.png" new file mode 100644 index 0000000..75627b4 Binary files /dev/null and "b/Chapter7/\354\242\213\354\235\200 \354\275\224\353\223\234, \353\202\230\354\201\234 \354\275\224\353\223\234 (7) \354\275\224\353\223\234\353\245\274 \354\230\244\354\232\251\355\225\230\352\270\260 \354\226\264\353\240\244 f204d7f7a9574b49b712a8fcce33551c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-03-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_9.59.40.png" differ diff --git a/Chapter8/mangcho.md b/Chapter8/mangcho.md new file mode 100644 index 0000000..e2ab31a --- /dev/null +++ b/Chapter8/mangcho.md @@ -0,0 +1,104 @@ +# 좋은 코드, 나쁜 코드 (8) 코드를 모듈화하라 + +date: March 3, 2024 +preview: 좋은 코드, 나쁜 코드 8장을 읽고 정리한 글입니다. + +--- + +### 💭 의존성 주입의 사용을 고려하라 + +하위 문제에 대한 해결책이 항상 하나만 존재하는 것은 아니다. 재구성할 가능성이 있다면 의존성 주입(DIP)를 고려해보자. + + +위 예제에서 RouterPlanner는 RoadMap의 구현체인 NorthAmericaRoadMap에 의존하고 있다. 이렇게 되면 추상화 계층이 지저분해지고 코드는 변경에 대해 유연하지 못하다. + +이 문제는 다음과 같이 의존성 주입으로 해결할 수 있다. + + +그리고 다음과 같이 사용할 수 있다. + + +하지만 이 방식의 단점은 RoutePlanner를 생성하기 전에 RoadMap의 인스턴스를 생성해야 한다는 것이다. 코드가 지저분해지고 복잡하다. + +이 문제는 몇 가지 팩토리 함수를 제공하면 훨씬 더 쉽게 해결할 수 있다. 또한, 팩토리 메서드의 대안으로 의존성 주입 프레임워크를 사용할 수 있다. + + +**의존성 주입 프레임워크** + +- 의존성 주입 프레임워크를 사용하면 의존성 주입과 관련된 많은 작업을 수동으로 하지 않아도 되기 때문에 개발 작업이 쉬워진다. +- 프레임워크를 사용하면 팩토리 함수의 반복적인 코드를 작성하느라 허우적대지 않고, 대신 매우 모듈화되고 다용도로 사용할 수 있는 코드를 만들 수 있다. + +### 💭 인터페이스에 의존하라 + +구체적인 구현에 의존하면 변경이 제한된다. 가능한 경우 인터페이스에 의존하는 것이 좋다. 인터페이스에 의존하는 것이 더 간결한 추상화 계층과 더 나은 모듈화를 달성할 수 있는 길이다. + +### 💭 클래스는 자신의 기능에만 집중해야 한다 + +모듈화의 핵심은 변경 사항이 발생한 경우에 그 변경과 직접 관련된 코드만 수정한다는 것이다. 하나의 개념이 여러 클래스에 분산된다면, 이 목표는 깨진다. 이 상황은 클래스가 다른 클래스의 세부 사항에 지나치게 연관되면 발생한다. + +다음 코드에서 Book은 getChapterWordCount()는 Chapter와 관련된 내용을 알고 있다. + + +Chapter의 요구사항 변경은 Book에게까지 전파될 수 있다는 문제가 있다. 따라서, 다음과 같이 getChapterWordCount()를 Chapter 내부로 옮기는 것이 적절하다. + + +**디미터의 법칙** + +한 객체가 다른 객체의 내용이나 구조에 대해 가능한 한 최대한으로 가정하지 않아야 한다는 소프트웨어 공학의 원칙 + +### 💭 관련 있는 데이터는 함께 캡슐화하라 + +너무 많은 것들이 한 클래스 내부에 있을 때 문제점이 야기되지만 합리적인 경우에 그렇게 하기도 한다. 이를 통해 코드는 모듈화와 캡슐화를 달성할 수 있다. + +TextBox 클래스의 renderText()는 많은 인자를 전달 받는다. 이 중 관련된 것들을 하나의 클래스로 묶어서 캡슐화하는 것이 좋다. + + +또한, TextBox는 상대적으로 하위 수준의 코드일 가능성이 높다. 캡슐화하지 않는다면 상위의 코드 역시 수많은 매개변수를 넘겨야 할 것이다. + + +이에 대한 대안은 앞서 언급한 대로 관련성 있는 매개변수를 한 클래스로 묶는 것이다. + +### 💭 반환 유형에 구현 세부 정보가 유출되지 않도록 주의하라 + +구현 세부 정보가 유출되면 발생하는 문제 + +- 하위 계층에 대한 정보도 노출될 수 있다. +- 수정이나 재설정이 매우 어려워질 수 있다. + +다음 예제는 구현 세부 정보가 유출되는 예이다. ProfilePictureService의 내부에서 ProfilePictureResult를 사용한다. ProfilePictureResult는 HttpResponse.Status를 사용한다. 따라서 ProfilePictureService는 HttpResponse.Status를 사용한다는 정보가 유출된다. + + + +- ProfilePictureService 클래스를 사용하고 싶은 개발자는 HttpResponse와 관련된 여러 개념을 처리해야 한다. HTTP 상태 코드는 정말 많다. +- ProfilePictureService 클래스의 구현을 변경하는 것은 매우 어렵다. 내부 메서드를 사용하는 모든 코드가 HttpResponse 클래스를 처리해야 하고, 이것은 코드 계층에 의존성이 발생한 것이다. + +이 문제를 해결하는 방법은 추상화 계층에 적합한 유형을 반환함으로써 다른 개발자에게 노출되는 개념이 최소화 하는 것이다. + +다음은 반환 유형과 추상화 계층을 일치시키는 결과 반환 클래스를 생성한 결과이다. + + +### 💭 예외 처리 시 구현 세부 사항이 유출되지 않도록 주의하라 + +호출하는 쪽에서 복구하고자 하는 오류에 대해 비검사 예외를 사용하는 경우, 예외 처리 시 구현 세부 정보를 유출하는 것은 큰 문제가 될 수 있다. + + +위 코드의 주석은 구현 세부 사항으로 바람직하지 않다. 이것은 추상화 계층 개념을 위반할 뿐만 아니라 신뢰할 수 없고 오류를 일으키기 쉽다. TextSummarizer 클래스는 TextImportanceScorer 인터페이스에 의존하므로 이 인터페이스를 구현하는 어떤 클래스로도 설정할 수 있다. ModeLBasedScorer는 이러한 구현 클래스 중 하나일 뿐이지 유일한 클래스는 아니다. TextSumarizer는 TextImportanceScorer를 구현하는 다른 클래스로 설 +정할수있고, 그 클래스는 완전히 다른 유형의 예외를 발생시킬 수도 있다. + + +해결책은 추상화 계층에 적절한 예외를 만드는 것이다. 하지만 코드 줄이 더 많아진다는 단점이 있다. 그래도 장단점을 비교했을 때, 얻는 것이 훨씬 더 크다. + + + +### 💭 요약 + +- 코드가 모듈화되어 있으면 변경된 요구사항을 적용하기 위한 코드를 작성하기가 쉽다. +- 모듈화의 주요 목표 중 하나는 요구사항의 변경이 해당 요구사항과 직접 관련된 코드에만 영향을 미치도록 하는 것이다. +- 코드를 모듈식으로 만드는 것은 간결한 추상화 계층을 만드는 것과 깊은 관련이 있다. +- 다음의 기술을 사용하여 코드를 모듈화할 수 있다. + - 의존성 주입 + - 구체적인 클래스가 아닌 인터페이스에 의존 + - 클래스 상속 대신 인터페이스 및 구성의 활용 + - 클래스는 자신의 기능만 처리 + - 관련된 데이터의 캡슐화 + - 반환 유형 및 예외 처리 시 구현 세부 정보 유출 방지 \ No newline at end of file diff --git a/Chapter9/mangcho.md b/Chapter9/mangcho.md new file mode 100644 index 0000000..61f89d3 --- /dev/null +++ b/Chapter9/mangcho.md @@ -0,0 +1,62 @@ +# 좋은 코드, 나쁜 코드 (9) 코드를 재사용하고 일반화할 수 있도록 하라 + +date: March 3, 2024 +preview: 좋은 코드, 나쁜 코드 9장을 읽고 정리한 글입니다. + +--- + +### 💭 가정을 주의하라 + +가정은 코드 재사용 시 버그를 초래할 수 있다. 가정은 코드를 간결하게 하기도 하지만 불필요한 사용은 자제해야 한다. + + +가정이 필요하면 다음과 같이 해라. + +- 가정이 ‘깨지지 않게’ 만들라. +- 오류 전달 기술을 사용하라. + +다음은 한 가지 예시이다. 어서션을 통해서 Article을 사용하려는 개발자에게 가정을 명확하게 알리고 있다. + + +### 💭 전역 상태를 주의하라 + +전역 상태의 문제점은 재사용하기에 안전하지 않다는 점이다. 이유는 상태를 계속 갖고 있기 때문이다. 여러 인스턴스에서 전역 상태를 임의로 수정하는 상황에서 의도치 않은 결과가 발생하기 쉽다. 이를 해결하는 방법은 전역 상태를 인스턴스의 상태로 바꾸고 의존성을 주입하는 것이다. + +### 💭 기본 반환값을 적절하게 사용하라 + +낮은 계층에서 코드의 기본 반환값은 재사용성을 해친다. 낮은 계층에서부터 기본 반환값이 올라온다면, 그에 대한 의존성으로 상위 계층도 동일하게 기본 반환값을 전달하기 때문이다. + + +### 💭 함수의 매개변수를 주목하라 + +필요 이상으로 매개변수를 받는 함수는 재사용하기 어려울 수 있다. + +다음 코드에서 setTextColor()는 TextOptions에 텍스트 색상만 사용한다. 하지만 TextOptions를 통째로 매개변수로 전달받고 있다. + + +이 코드의 문제는 호출하는 부분에서 드러난다. + + +이에 대한 해결책은 필요한 것만 매개변수로 받는 것이다. + + +### 💭 제네릭의 사용을 고려하라 + +다음이 제네릭의 예시이다. + + +제네릭이 아닌, 특정 유형에 의존하면 일반화를 제한한다. 다음 코드는 values가 String에 의존하고 있어서 재사용이 어렵다. + + +다음과 같이 제네릭을 사용하면 특정 클래스에 대한 의존성이 낮아지고, 재사용성이 높아진다. + + +### 💭 요약 + +- 동일한 하위 문제가 자주 발생하므로 코드를 재사용하면 미래의 자신과 팀 동료의 시간과 노력을 절약할 수 있다. +- 다른 개발자가 여러분이 해결하려는 문제와는 다른 상위 수준의 문제를 해결하더라도 특정 하위 문제에 대해서는 여러분이 작성한 해결책을 재사용할 수 있도록 근본적인 하위 문제를 식별하고 코드를 구성하도록 노력해야 한다. +- 간결한 추상화 계층을 만들고 코드를 모듈식으로 만들면 코드를 재사용하고 일반화하기가 훨씬 쉽고 안전해진다. +- 가정을 하게 되면 코드는 종종 더 취약해지고 재사용하기 어렵다는 측면에서 비용이 발생한다. + - 가정을 하는 경우의 이점이 비용보다 큰지 확인하라. + - 가정을 해야 할 경우 그 가정이 코드의 적절한 계층에 대해 이루어지는 것인지 확인하고 가능하다면 가정을 강제적으로 적용하라. +- 전역 상태를 사용하면 특히 비용이 많이 발생하는 가정을 하는 것이 되고 재사용하기에 전혀 안전하지 않은 코드가 된다. 대부분의 경우 전역 상태를 피하는 것이 가장 바람직하다. \ No newline at end of file