😃 책에서 기억하고 싶은 내용을 써보세요.
- 함수를 만드는 첫째 규칙은 '작게'. 둘째 규칙은 '더 작게'
- if/else while 문에 들어가는 블록은 한 줄이어야 한다.
- 중첩 구조가 생길만큼 함수가 커져서는 안된다. 들여쓰기 수준이 1단 2단을 넘으면 안된다.
- 한 가지만 해라! 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
- 의미있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
- 함수당 추상화 수준은 하나로!
- 함수가 확실히 한 가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
- getHtml()은 추상화 수준이 아주 높고, render()은 추상화 수준이 중간이고, append()는 추상화 수준이 아주 낮다.
- 한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다.
- 함수가 확실히 한 가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
- 내려가기 규칙. 코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
- 각 함수는 다음 함수를 소개한다. 각 함수는 일정한 추상화 수준을 유지한다.
- Switch 문
- 함수가 길다.
- 한 가지 작업만 수행하지 않는다.
- Single Responsibility Principle 을 위반한다. 코드를 변경할 이유가 여럿이다.
- Open Closed Principle 을 위반한다. 새 직원 유형을 추가할 때마다 코드를 변경한다.
- => 따라서 추상 팩토리 Abstract Factory에 꽁꽁 숨긴다. 직원 클래스를 만들어서 파생 클래스와 그 함수들을 이용하라!
- 서술적인 이름을 사용하라. 이름이 길어도 괜찮다. 길고 서술적인 이름이 길고 서술적인 주석보다 낫다.
- 대신 일관성 있는 이름으로. 같은 문구, 같은 명사, 동사 등등
- isTestable() includeSetupAndTearDownPages()
- 이상적인 함수의 인수 개수는 0개(무항)이다. 3개는 피하는 것이 좋다. 4개는 사용하면 안 된다.
- why? 코드를 읽기도 어렵고, 테스트 케이스를 짜는데도 난감하다.
- 함수에 인수 1개를 넘기는 이유:
- 인수에 질문을 던지는 경우, 인수를 뭔가로 변환해 결과 반환하는 경우. 이벤트 함수는 조심해서 써라.(이름에 이벤트인 것이 명확해야 함) - 이 경우들이 아니라면 단항 함수는 가급적 피한다.
- 플래그 인수는 추하다. 함수로 부울 값을 넘기는 관례는 끔찍하다. 함수가 한번에 여러 가지를 한다고 대놓고 말하는 셈.
- 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다. writeField(name), assertExpectedEqualsActual(expected, actual)
- 2항 함수:
- 보통 단항 함수보다 이해하기 어렵다. 적절한 경우: 좌표계 표현할 때.
- 인수의 순서에 유의하기. 가능하면 단항함수로 변환하라.
- 3항 함수:
- 이항 함수보다 훨씬 이해가 어렵다. 3항함수를 만드는 것은 아주 신중히 고려하라.
- 인수가 2-3개 필요하다면 클래스 변수를 선언하는 것을 생각해보라.
- 때로는 인수개수가 가변적인 함수도 필요하다. e.g. String.format("%s worked %.2f" hours." name, hours);
- => 사실 상 이것은 2항 함수다. public String format(String format, Object... args)
- 부수 효과를 일으키지 마라!
- 예상치 못하게 클래스 변수 수정. 함수로 넘어온 인수나 시스템 전역 변수 수정 등등.
- e.g. 함수 checkPassword(str userName, str password){}가 함수 내에서 Sesssion.initialize()를 수행하고 있다.
- 그러나 함수의 이름만 봐서는 세션 초기화한다는 사실이 드러나지 않는다. 이런 경우에 함수 이름은 checkPasswordAndInitSession()이 더 낫다. 물론 함수가 한 가지만 한다는 규칙을 위반한다.
- 명령과 조회를 분리하라. 객체 상태를 변경하거나 객체 정보를 반환하거나 둘 중 하나만 해야 한다.
- 오류 코드보다 예외를 사용해라.
- if (deletePage(page)== E_OK)보다는 예외 사용.
try { deletePage(page); ... } catch (Exception e) { ... }
- 그러나 try/catch 블록도 추하다. 코드 구조에 혼란. 정상 동작과 오류 동작을 뒤섞는다. 따라서 try/catch를 별도 함수로 뽑아내라. 정상 동작과 오류 처리 동작을 분리하면 코드를 이해하고 수정하기 쉽다.
private void deletePangeAndAllReferences(Page page) throws Exception { deletePage(page) ... } private void logError(Exception e){ logger.log(...) }
- Error Enum 만들어서 자주 사용하는데 이는 코드 의존성을 높일 뿐. (다른 클래스에서 이넘 import 해야하니까) 따라서 새 오류 코드 추가하는 대신 그냥 기존꺼 사용하는 경향이 높아진다. 따라서 그냥 예외 처리를 해 준다면 재컴파일/재배치 없이도 새 예외 클래스 추가 가능하다.
- if (deletePage(page)== E_OK)보다는 예외 사용.
- 반복하지 마라!
- SetuptTeardownIncluder라는 클래스를 정의하고, page는 클래스 변수로.
- includeSetupPages(), includeTeardownPages() 라는 각각의 함수를 만들고, 그 함수들을 실행하는 큰 함수를 만든다.
🤔 오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요
- 내가 회사 코드 짜면서 자주 하는 행동들(함수 하나가 여러개 처리, 불리언 인수 사용 등)이 클린코드의 기본 원칙에 위배되는 것이였다니! 허허 내 코드를 리뷰해준 다른 동료들에게 다시 한 번 감사의 마음이 든다.
- 함수 한 개가 하나의 펑션을 수행한다는 것이 정말 기본적인 원칙이라는 걸 깨달았다. 간단한데 그동안 지키지 않았던 것들이라 참 아쉽다.
- 추상화 수준이 동일해야 한다는 것은 그동안 생각치도 못했던 것들이다. 인식은 하고 있었는데 그게 문제라는 것을 자각하지 못하고 있다가 이 부분을 읽을 때 내가 썼던 코드들이 주마등처럼 스쳐 지나갔다.
- 추상 클래스를 만드는 방식은 앞으로 좀 활용해봐야겠다.
🔎 궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
- 없음
'Clean Code' 카테고리의 다른 글
6장 객체와 자료구조 (1) | 2022.04.20 |
---|---|
5장 형식 맞추기 (0) | 2022.02.27 |
4장 주석 (3) | 2022.01.31 |
2장 의미 있는 이름 (0) | 2022.01.25 |
1장 깨끗한 코드 (0) | 2022.01.22 |