Skip to content

Latest commit

 

History

History
262 lines (196 loc) · 18.9 KB

clean-code.md

File metadata and controls

262 lines (196 loc) · 18.9 KB

목차

03 장

작게 만들어라

함수를 만드는 첫째 규칙은 작게다 함수를 만드는 둘째 규칙은 더 작게다. 함수가 작을수 록 더좋다는 증거나 자료를 제시하기도 어렵다.

블록과 들여쓰기

if, else, while 문 등에 들어가는 블록은 한 줄이어야 한다는 의미다.대개 거기서 함수를 호출한다. 그러면 바깥을 감싸는 함수가 작아질 뿐 아니라, 블록 안에서 호출하는 함수 이름을 적절히 짓는다면 코드를 이하하기도 쉬워진다.

이 말은 중첩 구조가 생길만큼 함수가 커져서는 안 된다는 뜻이다. 그러므로 함수에서 들어쓰기 수준은 1단이나 2단을 넘어서는 안된다 그래야 함수는 일고 이해하기 쉬워진다.

한가지만 해라

함수는 한 가지를 해야 한다. 그 한가지를 잘 해야한다. 그 한 가지만을 해야한다.

함수 당 추상화 수준은 하나로

서술적인 이름을 사용하라

  • 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
  • 이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름 보다 좋다
  • 길고 서술적인 이름이 길고 서술적인 주석보다 좋다.
  • 함수이름을 정할 때는 여러 단어가 쉡게 읽히는 명명법을 사용한다
  • 그럼 다음 여러 단어를 사용해서 함수 기능을 잘 표현하는 이름을 선택한다.

함수 인수

  • 무항 : 함수에서 이상적인 인수는 개수는 0개
  • 단항 : 그 다음은 1개,
  • 이항 : 그 다음은 2개
  • 3개는 가능한 피하는 편이 좋다
  • 4개 이상은 특별한 이유가 필요하다. 특별한 이유가 있어도 사용하면 안된다.

많이 쓰는 단항 형식

부수 효과를 이르키지 마라!

명령과 조회를 분리 하라

  • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다. 둘 다 하면 안된다.
  • 객체 상태를 변경하거나 객체 정보를반환 하거나 둘 중 하나다
  • 둘 다 하면 혼란을 초래한다.

형식 맞추기

신문 기사처럼 작성하라

  • 이름은 간단하면서도 설명이 가능하게 짓는다.
  • 이름만 보고 올바른 모듈을 살펴보고 있는지 아닌지를 판단할 정도로 신경써서 짖느다.
  • 소스 파일 첫 부분은 고차원 개념과 알고리즘을 섦ㅇ한다.
  • 아래로 내려갈수록 의도를 세세하게 묘사한다.
  • 마지막에는 가장 저아춴 함수와 세부 내역이 나온다

개념은 빈 행으로 분히하라

  • 생각 사이에는 빈 행을 넣어 분리해야 마땅하다.

세로 밀집도

  • 줄바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다. 즉 서로 밀접한 코드 행은 세로로 가까이 놓아야 한다는 뜻이다.

객체와 자료 구조

null을 반환 하지마라.

단위 테스트

F.I.R.T

Fast : 빠르게

테스트는 빨라야 한다. 테스트는 빨리 돌아야 한다는 말이다. 테스트가 느리면 자주 돌림 엄두를 못 못낸다. 자주 돌리지 않으면 초반에 문제를 찾아내 고치짖 못한다. 코드를 마음 껏 정리하지 못한다. 결국 코드 품질이 망가지지 시작한다.

Independent :

각 테스트는 서로 의존하면 안 된다. 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안된다. 각 테스트는 독립저긍로 그리고 어 떤 수서로 실행 해도 괜찮아야 한다. 테스트가 서로에게 의존하면 하나가 실패할 때 나머지도 잇달아 실패하므로 원인을 진단하기 어려워지며 후반 테스트가 찾아내야 할 결함이 숨겨진다.

Repeatable : 반복가능하게

테스트는 어떤 환경에도 반복 가능해야 한다. 실제 환경, QA 환경, 버스를 타고 집으로 가는 길에 사용하는 노트북에서도 실행할 수 있어야 한다. 테스트가 돌아가지 않는 환경에 하나라도 있다면 테스트가 실패한 이유를 둘어낼 변명이 생긴다. 게다가 환경이 지원되지 않기에 테스트를 수행하지 못하는사황에 직면한다.

Self-Validating : 자가검증하는

테스트는 bool 값으로 결과를 내야 하다. 성공 아니면 실패다. 통과 여부를 알려고 로그 파일을 일게 만들어서는 안된다. 통과 여부를 보려고 텍스트 파일을 두 개를 수작업으로 비교하게 만들어서도 안된다.

Timely : 적시에

테스트는 적시에 작성해야 한다. 단 위 테스트는 단위테스트하려는 실제 코드를 구현하기 직전에 구현한다. 실제 코드를 구현한 다음에 테스트 코드를 만들면 실제 코드가 테스트하기 어렵다는 사실을 반견할지도 모른다. 어떤 실제 코드는 테스트하기 ㅓ무 어렵다고 판명날지도 모른다. 테스트가 불가능하도록 실제 코드를 솔계할지도 모른다.

클래스

클래스 체계

클래스를 정의하는 펴준 자바 관례에 따르면, 가장 먼저 변수 목록이 나온다. 정적 공개 상수가 있다면 맨 처음에 나온다 다음으로 정적 비공개 변수가 나오며 이어서 공개 인스턴스 변수가 나온다 공개 변수가 필요한 경우는 거의 없다 변수 목록 다음에는 공개 함수가 나온다. 비공개 함수는 자신을 호출하는 공개 함수 직후에 넣는다. 즉 추상화 단계가 순차적으러 내려간다. 그래서 프로그램은 신문 기사처럼 읽힌다.

캡슐화

변수와 유틸리티 함수는 가능한 공개하지 않는 편이 낫지만 반드시 숨겨야한다.는 법칙도 없다. 때로는 변수나 유틸리티 함수를 protected로 선언해 테스트 코드에 접근을 허용하기도 한다. 우리에게는 테스트는 아주 중요하다. 같은 패키지 안에서 테스트 코드가 함수를 호출하거나 전체 공개한다. 하지만 그 전에 비공개 상태를 유지할 온갖 방법을 강구한다. 캡슐화를 풀어주는 결정은 언제나 최후의 수단이다.

클래스는 작아야한다.

클래스를 만들 때 첫 번째 규칙은 크기다. 클래스는 작아야 한다. 두 번 째 규칙도 크기다. 더 작아야한다. 앞어 함수 장에서 했던 이야기를 되풀이할 의도는 없다. 가장 먼저 떠오르는 의문은 함수와 마찬가지로 얼마나 작아야 하는 가 겠다 함수는 물리적인 행 수로 크기를 측정했다. 클래스는 다른 척도를 사용한다. 캘래스가 맡은 책임을 센다

냄새와 휴리스틱

주석

C1: 부적절한 정보

다른 시스템 (이슈 관리 소스관리 등등...) 저장할 정보는 주석으로 적절하지 못한다. 예를 들어 변경 이력은 장황한 날짜와 따분한 내용으로 소스 코드만 먼잡하게 만든다.

C2: 쓸모 없는 주석

오래된 주석, 엉뚱한 주석, 잘못된 주석은 더이상 쓸모가 없다. 주석은 빨리 낡는다. 쓸모 없어질 주석은 아예 달지 않는 편이 가장 좋다. 쓸모 없는 주석은 재 빨리 삭제하는 편이 가장 좋다.

C3: 중복된 주석

코드만으로 충분한데 구구절절 설명하는 주석이 중복된 주석이다.

C4: 성의 없는 주석

작성할 가치가 있는 주석은 잘 잘성할 가치도 있다. 주절대지 않는다. 당연한 소리를 반복하지 않는다. 간단하고 명료하게 작성한다.

C5: 주석 처리된 코드

얼마나 오래된 코드인지, 중요한 코드인지 아닌지, 알길이 없다. 그렘에도 아무도 삭제하지 않는다. 누군가에게 필요하거나 다른 사람이 사용할 코드라고 생각 하기 때문이다. 주석을 처리된 코드를 발견하면 즉각 지워 버려라. 걱정할 필요 없다. 소스 코드 관리 시스템이 기억하니까.

환경

E1: 여러단계로 빌드해야 한다.

빌드는 간단히 한 단계로 끝나야 한다. 소스 코드 관리 시스템에서 이것저럿 따로 체크아웃할 필요가 없아야한다.

E2: 여러 단계로 테스트 해야한다.

모든 단위 테스트는 한 명령어로 돌여야 한다. IDE에서 버튼하나로 모든 테스트를 돌린다면 가장 이상적이다. 아무리 열락한 환경이라도 셀에서 명령 하나로 가능해야한다. 모든 테스트를 한 번에 실행하는 능력은 아주 근복적이고 아주 중요하다.

함수

F1: 너무 많은 인수

함수에서 인수 개수는 작을수록 좋다. 아예 없으면 가장 좋다. 다음으로 하나, 둘 셋이 차례로 좋다. 넷 이상은 그 가치가 아주 의심스러움으로 최대한 피한다. (50쪽 참고)

F2: 출력인수

출력 인수는 직관으로 정면으로 위배한다. 일반적으로 독자는 인수를 입력으로 간주한다. 함수에서 뭔가 상태를 변경해야 한다면 함수가 속한 객체의 상태를 변경한다(56 출력 임수 참고)

F3: 플래그 인수

boolean 인수는 함수가 여려 기능을 수행한다는 명백한 증거다. 플래그 인수는 혼란을 초래하므로 피래야 마땅하다.(52쪽 참고)

F4: 죽은 함수

아무도 호풀하지 않은 함수는 삭제한다. 죽은 코드는 낭비다. 과감히 삭제 하라.

일반

G1: 한 소스팔일에 여러 언어를 사용한다.

자바 소스파일에 XM, HTML, 등등 혼란스럽고 조잡하게 사용하지 마라

G2: 당연한 동작은 구현하지 않는다.

이해 못했음...

G3: 경게를 올바로 처리하지 않는다.

코드는 올바로 동작해야 한다. 너무나도 당연한 말이다. 그런데 우리는 올바른 동작이 아주 복잡하다는 사실을 자주 간과한다. 흔히 개발자들은 머릿속에서 코드를 돌려보고 끝낸다. 자신의 직관에 의존할 뿐 모든 경계와 구석진 곳에서 코드를 증명하려 애쓰지 않는다. 부지런함을 대신할 지름길은 없다. 모든 경계 조건, 모든 구석진 곳, 모든 기벽, 모든 예외를 우아하고 직관적인 알고리즘을 좌초 시킬 암초다. 스스로의 직관에 의존하지마라. 모든 경계 조건을 찾아내고, 모든 경계 조건을 테스트하는 테스트 케이스를 작성하라.

G4: 안전 절차 무시

실패하는 테스크 케이스를 일단 제껴두고 나중으로 미루는 태도는 신용카드가 공짜 돈이라 생각하는 만큼 위험하다.

G5: 중복

이 책에 나오는 가장 중요한 규칙 중 하나이다. 코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라. 중복된 코드를 하위 루틴이나 다른 클래스로 분리하라. 이렇듯 추상화로 중복을 정리하면 설계 언어의 어휘가 늘어난다.

가짱 뻔뻔한 유형은 똑같은 코드가 여러 차례 나오는 중복이다. 프로그래머가 미친듯이 마우스로 긁어다가 여기저기로 복사한 듯이 보이는 코드이다. 이런 중복은 간단한 함수로 교체한다. 좀 더 미묘한 유형은 여러 모듈에서 이련의 switch/case 나 if/else 문으로 똑같은 조건을 거듭 확인하는 중복이다. 이런 중복은 다형성으로 대체해야한다.

G6: 추상화 수준이 올바르지 못하다.

추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다. 때로 우리는 (고차원 개념을 표현하는) 추상 클래스와 (저차원 개념을 표현하는) 파생 클래스를 생성해 추상화를 수행한다. 추상화 개념을 분리할 때는 척저해야한다. 모든 저차원 개념은 파생 클래스에 넣고, 모든 고차원 개념은 기초 클래스에 넣는다.

G7: 기초 클래스가 파생 클래스에 의존한다.

개념을 기초 클래스와 파생 클래스로 나누는 가장 흔한 이유는 고차원 기초 클래스 개념을 저차원 파생 클래스 개념으로부터 분리해서 독립성 보장하기 위해서다. 그러므로 기초 클래스가 파생 클래스를 사용한다면 뭔가 문제가 있ㄷ는 말이다. 일반적으로 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다. 물론 예외는 있다.

G8: 과도한 정보

잘 정의된 모듈은 인터페이스가 아주 작다. 하지만 작은 인터페이스로도 많은 동작이 가능하다. 부실하게 정의된 뫼듈은 인터페이스가 구질구질하다. 그래서 간단한 동작 하나에 온갖 인터페이스가 필요하다. 잘 정의된 인터페이스는 많은 함수를 제공하지 않는다. 그래서 결합도가높다. 우수한 소프트웨어 개발자는 클래스나 모듈 인터페이스에 노출할 함수를 제한할 줄아야야한다. 클래스가 제공하는 메소드는 수는 작을 수록좋다. 함수가 아는 변수 수도 작을수록 좋다.

G9: 죽은코드

죽은 코드란 실행되지 않는 코드를 가리킨다. 불가능한 조건을 확인하는 if문과 throw 문이 없는 try 에서 catch 블록이 좋은 예다. 아무도 호출하지 않은 유틸리티 함수와 switch/case 문에 불가능한 조건도 또 다른 좋은 예다.

G10: 수직 분리

변수와 함수는 사용되는 위치에 가깝게 정의한다. 지역 변수는 처음으로 사용하기 직전에 선언하며 수직으로 가까운 곳에 위치해야한다. 선언한 위치로부터 몇백 줄 아래에서 사용하면 안된다. 비공개 함수는 처음으로 호출한 직후에 정의한다. 비공개 함수는 전체 크래스 범위에 속하지만 그래도 정의하는 위치와 호출하는 위치를 가깝게 유지한다. 비공개 함수는 처음으로 호출되는 위치를 찾은 후 조금만 아래로 내려가면 쉽게 눈에 띄어야한다.

G11: 일관성 부족

어떤 개념을 트겆ㅇ 방식으로 구현했다면 유사한 개념으로 같은 방식으로 구현한다. 간단한 일관성만으로 코드를 읽고 수정하기가 대단히 쉬워진다.

G12: 잡동사니

비어 있는 기본 생성자가 왜 필요한가? 쓸데 없이 코드만 복잡하게 만든다. 아무도 사용하지 않는 변수, 아무도 사용하지 않은 함수 제거해야한다.

G13: 인위적 결합

서로 무간한 개념을 인위적으로 결합하지 않는다. 예를들어, 일반적인 enum은 특정 클래스에 속할 이유가 없다. enum이 클래스에 속한다면 enum을 사용하는 코드가 특정 클래스를 알아야만한다. 범용 static 함수도 마찬가지로 특정 클래스에 속할 이유가 없다. 함수, 상수 , 변수를 선언할 때는 시간을 들여 올바른 위치를 고민한다. 그저 당장현한 곳에 선언하고 내버려두면 안된다.

G14: 기능 욕심

클래스 메서드는 자기 클래스의 변수와 함수애 관심을 가져야지 다른 클래스의 변수와 함수에 관심을 가져서는 안된다. 메서드는 다른 객체의 참조자와 변경자를 사용해서 그 객체내요을 조작한다면 메서드가 그 객체 클래스의 범위를 욕심내는 탓이다.

G15: 선택자 인수

함수 호출 끝에 달리는 false 인수만큼 밉살스런 코드도 없다. 선택자 인수는 목적을 기억하기 어려울 뿐 아니라 각 선택자 인수가 여러 함수를 하나로 조합한다. 선택자 인수는 큰 함수를 작은 함수 여럿으로 쪼개지 않으려는 게으름의 소산이다. 일반적으로 인수를 넘겨 동작을 선택하는 대신에 새로운 하수를 만드는 편이 좋다.