클린 코드 3장 & 4장
3장 함수
어떤 프로그램이든 가장 기본적인 단위는 함수이다. 읽기 쉽고 이해하기 쉬운 함수는 어떻게 작성해야하는가?
작게 만들어라!
함수는 짧아야한다.
Norminette
블록과 들여쓰기
함수 안의 indent 레벨은 두 단계를 넘어서는 안된다.
한 가지만 해라!
함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한가지만을 해야 한다.
지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 하는 것이다.
의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
⇒ 더이상 함수를 추출할 수 없을때까지 작업들을 함수로 분리해내자.
함수 당 추상화 수준은 하나로!
A하려면 B, C를 해야한다.
B하려면 D해야한다.
D하려면 F, G해야한다.
F하려면 H해야한다.
G하려면 I해야한다.
C하려면 E해야한다.
E하려면 J해야한다.
함수로 분리해 한 함수 당 추상화 수준을 하나로 만들어주자.
내려가기 규칙
코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
핵심은 짧으면서도 ‘한 가지’만 하는 함수이다. ⇒ 라인자체도 짧고 추상화 수준이 하나여야한다.
Switch 문
switch 문은 작게 만들기 어렵다. (if/else가 여럿 이어지는 구문도 포함) 또한 ‘한 가지’ 작업만 하는 switch 문도 만들기 어렵다. 원래 switch문은 N가지를 처리한다. switch 문을 완전히 피할 방법은 없다.
⇒ 각 switch문을 저차원 클래스에 숨기고 절대로 반복하지 않는 것으로 극복 (다형성 이용)
서술적인 이름을 사용하라!
함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
길고 서술적인 이름 > 짧고 어려운 이름
길고 서술적인 이름 > 길고 서술적인 주석
이름을 정할 때 시간이 많이 들어도 괜찮다. 이때 들이는 시간이 아무리 길어도 읽기 어렵게 짓고 훗날 다시 해석하느라 쓰는 시간보다는 짧다.
서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기가 쉬워진다.
이름을 붙일 때는 일관성이 있어야 한다. 모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다.
includeSetupAndTeardownPages
, includeSetupPages
, includeSuiteSetupPage
, includeSetupPage
등등…
문체가 비슷하면 이야기를 순차적으로 풀어가기도 쉬워진다. 코드가 읽는사람이 짐작하는 대로
작성되어야한다.
함수 인수
인수는 적을수록 좋다. 인수는 개념을 이해하기 어렵게 만든다. 코드를 읽는 사람이 인수의 의미를 해석해야한다. 추상화 수준이 달라질 위험도 있다.
테스트 관점에서도 갖가지 인수 조합으로 함수를 검증하는 TC를 만들어야 하기에 복잡해진다.
출력 인수는 이해하기 어렵다. 대개 함수에서 인수로 결과를 받으리라 기대하지 않는다. 출력 인수는 코드를 재차 확인하게 만든다.
많이 쓰는 단항 형식
- 인수에게 질문을 던지는 경우
boolean fileExists("MyFile")
- 인수를 뭔가로 변환해 결과를 반환하는 경우
InputStream fileOpen("MyFile")
- 이벤트
passwordAttemptFailedNtimes(int attempts)
위 경우들이 아니라면 단항 함수는 가급적 피한다. 입력 인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려주자.
플래그 인수
함수로 부울 값을 넘기는 짓은 입력값이 참인지 거짓인지에 따라 하는 행동이 다르다는 말이므로 함수가 한꺼번에 여러가리를 처리한다는 말이다. 그냥 함수를 두개 작성하자.
이항 함수
인수가 2개인 함수는 인수가 1개인 함수보다 이해하기 어렵다. 인수의 순서의 이해, 인수를 무시해야 하는지 판단하는 데에 시간이 소요된다. ⇒ 결국 문제를 일으킨다.
예외 ) 좌표계 점을 인수로 받는 경우 Point p = new Point(0, 0)
가능하면 단항 함수로 바꾸도록 애써야한다.
writeFiled(ouputStream, name)
⇒ outputStream.writeField(name)
삼항 함수
인수가 3개인 함수는 인수가 2개인 함수보다 훨씬 더 이해하기 어려우며 더 많은 문제를 야기한다.
예외 ) 부동소수점 비교 assertEquals(1.0, amount, .001)
인수 객체
인수가 많이 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 짚어보자.
Circle makeCircle(double x, double y, double radius);
⇒ Circle makeCircle(Point center, double radius);
개념을 표현하여 묶어주자!
인수 목록
가변 인수 전부는 List 형 인수 하나로 취급할 수 있다.
String.format("%s worked %.2f hours.", name, hours);
실제 String.format의 선언부
⇒ public String format(String format, Object... args)
하지만 이를 넘어서는 인수를 사용할 경우에는 문제가 있다.
void triad(String name, int count, Integer... args);
동사와 키워드
함수의 이름과 인수가 동사/명사 쌍을 이뤄야 한다.
write(name)
: ‘이름’이 무엇이든 ‘쓴다’.
writeField(name)
: ‘이름’이 ‘필드’라는 사실이 분명하게 드러남.
함수 이름에 키워드를 추가하자. 함수 이름에 인수 이름을 넣으면 인수 순서를 기억할 필요가 없어진다.
assertExpectedEqualsActual(expected, actual)
부수 효과를 일으키지 마라!
Side effect : 함수 내에서 함수 외부에 영향을 끼치는 것
부수 효과는 시간적인 결합이나 순서 종속성을 초래한다. 즉 함수가 특정 상황에서만 호출이 가능해지는 것이다. 자칫 잘못되면 의도하지 않은 결과를 만들 수도 있다.
만약 시간적인 결합이 필요하다면 함수 이름에 분명히 명시해주자.
checkPasswordAndInitializeSession
명령과 조회를 분리하라!
함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.
나쁜 코드 ⇒ public boolean set(String attribute, String value);
이 함수는 이름이 attribute인 속성을 찾아 값을 value로 설정하고 성공하면 true, 실패하면 false를 반환한다.
if(set(”username”, “unclebob”))…
위 코드는 username을 unclebob으로 설정하는지, username이 unclebob인지 확인하는지 모호하다.
⇒
if(attributeExists("username")) {
setAttribute("username", "unclebob");
...
}
오류 코드보다 예외를 사용하라!
try/catch
나쁜코드
좋은 코드
반복하지 마라!
코드 길이가 늘어날 뿐 아니라 알고리즘이 변하면 중복되는 곳을 모두 손봐야한다. 어느 한곳이라도 빠뜨리는 바람에 오류가 발생할 확률도 올라간다.
구조적 프로그래밍
에츠허르 데이크스트라 - 모든 함수와 함수 내 모든 블록에 입구와 출구는 하나씩만 존재해야한다.
⇒ 함수는 return 문이 하나여야 한다. 루프 안에서 break나 continue를 사용해서는 안된다. 특히 goto는 절대 사용해서는 안된다.
But, 함수의 크기가 작다면 의도를 쉽게 표현하기 위해 return, break, continue를 여러 차례 사용해도 괜찮다.
함수를 어떻게 짜죠?
글을 쓸 때 초안은 대게 서투르고 어수선하다. 원하는 대로 읽힐 때까지 말을 다듬고 문장을 고치고 문단을 정리한다.
함수도 처음에는 길고 복잡하다. 들여쓰기 단계도 많고 이름은 즉흥적이며 중복, 인수 목록도 길다.
→ 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거한다. 메서드를 줄이고 순서를 바꾼다. 전체 클래스를 쪼개기도한다.
처음부터 탁 짜내는 것이 가능한 사람은 없다.
결론
시스템은 구현할 프로그램이 아니라 풀어갈 이야기이다. 함수가 분명하고 정확한 언어로 깔끔하게 같이 맞아떨어져야 이야기를 풀어가기가 쉬워진다.
4장 주석
나쁜 코드에 주석을 달지 마라. 새로 짜라.
주석은 나쁜 코드를 보완하지 못한다
난장판을 주석으로 수습하려하지말고, 난장판을 수습하자!
코드로 의도를 표현하라!
Bad
Good
코드만으로 대다수의 의도를 표현할 수 있다!
좋은 주석
- 법적인 주석
- 정보를 제공하는 주석
- 의도를 설명하는 주석
- 의미를 명료하게 밝히는 주석
- 결과를 경고하는 주석
- TODO 주석
- 중요성을 강조하는 주석
- 공개 API에서 Javadocs
나쁜 주석
- 주절거리는 주석
- 같은 이야기를 중복하는 주석
- 오해할 여지가 있는 주석
- 의무적으로 다는 주석
- 이력을 기록하는 주석
- 있으나 마나 한 주석
- 무서운 잡음
- 함수나 변수로 표현할 수 있다면 주석을 달지 마라
- 위치를 표시하는 주석
- 닫는 괄호에 다는 주석
- 공로를 돌리거나 저자를 표시하는 주석
- 주석으로 처리한 코드
- HTML 주석
- 전역 정보
- 너무 많은 정보
- 모호한 관계
- 함수 헤더
- 비공개 코드에서 Javadocs
Leave a comment