클린코드 (~ 225p)

  • 보이스카우트 규칙

    캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라

    • 시간이 지날수록 코드가 좋아지는 프로젝트
    • 지속적 개선

의미있는 이름

  • 의도가 분명한 이름
  • 발음하기 쉬운 이름
  • 검색하기 쉬운 이름
  • 클래스 이름은 명사
  • 메서드 이름은 동사
  • 한 개념에 한 단어를 사용하라
    • 똑같은 메서드를 클래스마다 fetch, retrieve, get 으로 제각각 부르면 혼란스럽다.
      • 이거 엄청 공감!
  • 한 단어를 두 가지 목적으로 사용하지 마라.
    • 기존 add 메서드가 모두 기존 값 두 개를 더하여 새로운 값을 만들었다고 가정할 때
    • 새로 작성하는 add 메서드가 집합 값에 하나를 추가하는 개념이라면
    • add 가 아니라 insertappend 라는 이름을 택하자

함수

  • 작게
  • 한가지만
  • 부수효과를 일으키지 마라
    • 한 가지만 하는 척 네이밍 해놓고 몰래 더 하지 마라

주석

  • 부정확한 주석은 아예 없는 주석보다 나쁘다.
  • 주석을 유지하고 보수하기는 현실적으로 불가능하니, 코드로 표현을 잘하고자 노력하는 것이 더 중요하다.
  • 짧고 한 가지만 수행하며 이름을 잘 붙인 함수가 주석으로 헤더를 추가한 함수보다 훨씬 좋다.

형식 맞추기

  • 코드 형식은 중요하다.
  • 코드 형식은 의사소통의 일환이다.
  • 신문 기사처럼 작성하라.
    • 위에서 부터 아래로, 최상단에는 표제
    • 세세한 사실은 숨기고 커다란 그림부터
    • 쭉 읽어 내려가면 세세한 사실이 조금씩 드러나도록
  • 개념은 빈 행으로 분리하라.
  • 반대로 밀접한 코드 행은 가까이에 놓여야 한다.

객체와 자료구조

  • 이 6장은 어렵다… 잘 와닿지가 않는다.
  • 모든 것이 객체라는 생각이 미신이다.
  • 때로는 단순한 자료 구조와 절차적인 코드가 가장 적합한 상황도 있다.
  • 객체 지향 코드에서 어려운 변경은 절차적인 코드에서 쉬우며, 절차적인 코드에서 어려운 변경은 객체지향 코드에서 쉽다.

오류 처리

  • Checked Exception 은 OCP 를 위반한다.
    • 메서드에서 checked exception 을 던졌는데, catch 블록이 세 단계 위에 있다면 그 사이 메서드 모두가 선언부에 해당 exception 을 정의해야 한다.
    • 즉, 하위단계 코드 변경 시 상위 코드의 선언부를 전부 고쳐야 한다는 것.
    • 최하위 함수에서 checked exception 발생 시 최상위 단계까지 연쇄적인 수정이 필요.
    • throws 경로에 위치하는 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화가 깨진다.
    • 때로는 (아주 위험한 에러라던가) checked exception 이 유용할 때가 있지만, 대부분의 경우 의존성이라는 비용이 이익보다 크다.
  • 외부 API 의 exception 을 wrapper 로 다룬다.
    • 외부 API 호출 시 다양한 에러를 우리는 외부 API 에러 하나로 퉁치고 싶을 때,
    • 해당 API 를 wrapper 클래스를 만들어서, try-catch 로 다양한 에러를 우리가 정의한 에러로 throw 하는 것만으로도 코드가 훨씬 깨끗해진다.
    • 외부 API 가 설계한 방식에 발목을 잡히지 않고, 프로그램 사용에 편리한 API를 정의하면 그만이다.
  • null return 의 최소화를 강조
    • Special case pattern
    • null 말고 기본 값을 return 할 수 있도록 class 정의해서 null return 을 피해보는 것이 유용할 경우도.
  • parameter 로 null 을 넘기는 것은 아주 안좋다.
    • 인자로 넘어온 null 을 적절히 처리하는 방법이 없다.
    • 애초에 넘기지 못하도록 금지하는 정책이 합리적.

깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야 한다. 이 둘은 상충하는 목표가 아니다.

경계

  • 이 책에서 말하는 경계란, 외부 코드와 우리 코드의 경계이다. 즉, 외부코드를 다루는 방법에 대한 장이다.
  • 학습 테스트
    • 프로그램에서 사용하려는 방식대로 외부 API 를 호출하여, 통제된 환경에서 API 를 제대로 이해하는지 확인
    • 쉽게 말해서 쓰고싶은 방식대로 API 를 호출해보고, 막히는 것들을 하나 하나 확인해가며 단위테스트를 작성해 보는 과정으로 보인다.
    • API 에서 필요한 지식만 확보하는 손쉬운 방법.
  • 통제하지 못하는 코드를 사용할 때는 너무 많은 투자를 하거나, 향후 변경 비용이 지나치게 커지지 않도록 각별히 주의해야 한다.
  • 경계에 위치한 코드는 깔끔히 분리한다.
    • 또한 기대결과를 정의하는 테스트 케이스를 작성한다.
    • 외부 패키지를 세세히 분석할 필요는 없고, 외부 패키지에 의존하는 대신 통제가 가능한 우리코드에 의존하는 편이 훨씬 좋다.

새로운 클래스로 경계를 감싸거나 ADAPTER 패턴을 사용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하자.

  • *ADAPTER 패턴 은 구글링 등 찾아서 읽어보았는데 우리가 흔히 사용하는 인터페이스 활용방식과 크게 다르지 않은 것 같다.
    • 이 장의 핵심은 외부 코드의 변경이나 수정 시에 영향도를 최소화 하자는 데 있다.

단위 테스트

  • 테스트 코드는 실제 코드 못지 않게 중요하다.
    • 즉, 테스트 코드도 클린 코드로 작성해야 한다는 뜻으로 보임.
    • 테스트 코드도 사고와 설계가 필요하다.
  • 테스트코드와 실제 코드의 표준은 다를 수 있다.
    • 테스트코드는 비교적 효율성을 중요하게 생각하지 않아도 되므로.
    • 이를테면 테스트 코드에서는 StringBuffer 를 쓰지 않고 String Concatenation Operator(“+”) 를 쓰는 것이 좋을 수도 있다는 것.
  • 테스트당 assert 문을 단 하나만 사용해야 한다는 일각의 의견도 있음.
    • 이것도 틀리지는 않은 것 같다고 한다. 이해하기 쉽고 빠르다는 확실한 장점이 있으므로.
    • 그렇다고 꼭 하나만 쓰라는 것은 아님.
  • 테스트당 개념은 하나!

  • F.I.R.S.T 규칙
    • Fast, 빠르게
    • Independent, 독립적
      • 테스트가 서로 의존하면 하나가 실패할 때 나머지도 잇달아 실패하고 후반 테스트가 어렵다.
    • Repeatable, 반복가능하게
      • 테스트는 어떤 환경에서도 반복 가능해야 한다.
      • 어쩌면 네트워크가 연결되지 않은 환경에서도 돌릴 수 있어야 한다.
    • Self-Validating
      • 테스트는 Bool 값으로 결과를 나타내야 한다, 테스트를 수작업으로 검증해서는 안되고 테스트가 스스로 성공과 실패를 가늠해야 한다.
    • Timely
      • 테스트는 적시에 작성해야 한다.
      • 테스트가 불가능 하도록 실제 코드를 설계할지도 모른다. + (내생각 - 시기를 놓치면 쌓이고, 더 안하게 되고..)
    • 이 규칙들은 진짜 엄청나게 좋은듯. 실무에서 잘 준수할 수 있을지 모르겠으나 노력해봐야겠다.

테스트 코드는 실제 코드 만큼이나 프로젝트 건강에 중요하다. 표현력을 높이고 간결하게 정리하자.

클래스

  • 클래스 체계
    • 표준 자바 관례에 따르면, 신문 기사처럼 아래의 순서로 정의한다.
    • static public 상수
    • static private 상수
    • private 인스턴스 변수
    • public 메서드
    • private 메서드는 자신을 호출하는 public 함수 직후에 넣는다.
  • 클래스는 작아야 한다.
    • SRP, 단일 책임 원칙.
    • 즉, 클래스를 수정하는 원인은 하나여야 한다.
    • 1을 고칠때도 A클래스, 2를 수정할때도 A클래스에 수정이 필요하다면 이는 SRP를 위반한 것이라고 볼 수 있겠다.
  • 클래스는 응집도가 높아야 한다.
    • 클래스의 인스턴스 변수를 사용하는 메서드들이 많다면, 그 클래스는 응집도가 높다.
    • 응집도를 유지하면 작은 클래스 여럿이 나온다.
      • 큰 함수를 작은 함수로 쪼개다보면, 파라미터가 많아지고, 파라미터를 줄이면(인스턴스 변수로 승격한다면) 응집도가 낮아지고, 이는 클래스를 분리해도 된다는 신호이다.
  • 저자의 경험적 근거에 의하면 클래스 일부에서만 사용되는 private 메서드는 코드를 개선할 잠재적인 여지를 시사한다.
    • 이런생각은 처음해 보는데 맞는 것 같음 진짜.
  • SRP 를 준수하면 OCP 도 준수하기 쉬워진다.
    • 새 기능 추가 시 기존 클래스의 수정 없이 클래스를 제자리에 끼워넣기만 하면 끝나는 것이 베스트.
  • 결합도를 줄이면 DIP 를 따르는 클래스가 나온다는데 이건 와닿지가 않았음
    • 기본적으로 내가 DIP 에 대한 이해가 부족한 듯.
    • DIP, 상세한 구현이 아닌 추상화에 의존해야 한다.

시스템

복잡성은 죽음이다. 개발자에게서 생기를 앗아가며, 제품을 계획하고 제작하고 테스트하기 어렵게 만든다. - 레이오지, MS CTO

  • 이 장도 정말 10% 도 못 얻어가는 것 같다.
  • 다음에 다시 읽어야 할 듯.

창발성

  • 창발성
    • 남이 모르거나 하지 아니한 것을 처음으로 또는 새롭게 밝혀내거나 이루어 내는 성질
    • by 네이버 국어사전
  • 켄트 벡은 아래 네 가지 규칙을 따르면 설계는 단순하다 고 말한다. 목록은 중요도 순이다.
    • 모든 테스트를 실행한다.
    • 중복을 없앤다.
    • 프로그래머 의도를 표현한다.
    • 클래스와 메서드 수를 최소로 줄인다.
  • 단순한 설계 규칙 1: 모든 테스트를 실행하라
    • 철저한 테스트가 가능한 시스템을 만들면, 더 나은 설계가 얻어진다.
    • “테스트 케이스를 만들고 계속 돌려라” 라는 간단하고 단순한 규칙을 따르면 시스템은 낮은 결합도와 높은 응집력이라는, 객체지향이 지향하는 목표를 저절로 달성한다.
  • 단순한 설계 규칙 2~4: 리팩터링
    • 테스트 케이스를 모두 작성하고 리팩터링을 하면, 코드 정리를 하면서 시스템이 깨질까 걱정할 필요가 없다. 테스트 케이스가 있으니까.
  • 중복을 없애라
    • 아주 작은 코드도 적극적으로 중복 제거를 하자.
    • 소규모 재사용 은 시스템 복잡도를 극적으로 줄여준다.
  • 표현하라
    • 더 나은 이름, 더 작은 함수/클래스
    • 자신의 작품에 조금만 더 주의를 기울이자. 주의는 대단한 재능이다.
  • 클래스와 메서드 수를 최소로 줄여라
    • 클래스와 메서드 크기를 줄이자고 조그만 클래스와 메서드를 수없이 만들게 될 수 있다.
    • 무의미한 클래스 수를 줄의라는 의미이고,
    • 우선순위가 가장 낮다. 테스트 케이스를 만들고 중복을 제거하고 의도를 표현하는 작업이 더 중요하다는 뜻이다.