3 minute read

Clean Code에 대하여

in Clean Code by Robert C. Martin

Ch3 함수

1. 작게 만들어라!

함수를 만드는 첫째 규칙은 ‘작게!’다.
함수를 만드는 두번째 규칙은 ‘더 작게!!’다.
모든 함수를 4줄 이내로 작성하라.

2. 블록과 들여쓰기

if, else, while 문 등에 들어가는 블록은 한 줄이어야한다.
대게 거기서 함수를 호출한다.
이렇게 하면 바깥을 감싸는 함수의 크기가 줄어들 뿐만 아니라,
안에 들어가는 함수의 이름을 적절히 적는다면, 코드를 이해하기도 쉬워진다.
함수에서 들여쓰기 수준은 1단, 아무리 많아도 2단이어야 한다.

3. 한 가지만 해라!!

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

한가지가 되는 기준은 바로 추상화 다.
지정된 함수 이름 아래서 추상화 수준이 하나인 단계만 수행한다면,
그 함수는 한가지 작업만 한다.

또 하나,
만약 단순히 다른 이름이 아니라 의미있는 이름으로 다른 함수를 추출할 수 있다면,
그 함수는 여러 작업을 하는 셈이다.

4. 함수 당 추상화 수준은 하나로!

함수가 확실히 ‘한 가지’ 작업만 하려면 함수 내의 모든 문장의 추상화 수준이
동일해야 한다. 한 함수 내에 추상화 수준을 섞으면, 코드를 읽는 사람이 헷갈린다.
특정 표현이 근본개념인지 아니면 세부사항인지 구분하기 어려운 탓이다.
하지만 문제는 여기서 끝이 아니다.
근본 개념과 세부사항을 섞다보면,
깨어진 창문처럼 사람들이 함수에 세부사항을 점점 더 추가한다.

5. 위에서 아래로 코드 읽기: 내려가기 규칙

코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
즉, 프로그램을 위에서 아래로 읽으면, 함수 추상화 수준이 한번에 한단계씩 낮아진다.
이것을 내려가기 규칙 이라고 부른다.

추상화 수준이 한 개인 함수를 구현하기란 쉽지않다.
핵심은 짧으면서도 ‘한 가지’만 하는 함수이다.
LOGO 언어의 TO 코드를 읽어내려가듯 코드를 구현하면
추상화 수준을 일관되게 유지하기 쉬워진다.

6. Switch 문

본질적으로 switch 문은 n가지를 처리하기 때문에 작게 만들기 어렵다.
하지만 각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다.
다형성(polymorphism)을 이용한다.

7. 서술적인 이름을 사용하라!

함수가 하는 일을 잘 표현하도록 이름을 지어라.
“코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다. - Ward Cunningham”
함수가 작고 단순할수록 서술적인 이름을 붙이기도 쉬워진다. 이름이 길어도 괜찮다.
이름을 붙일 때는 일관성이 있어야 한다.
모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다.

8. 함수 인수

함수의 이상적인 인수 개수는 0개다.
다음이 1개, 그 다음이 2개다.
3개 이상의 인수는 피하는 것이 좋다.

9. 많이 쓰는 단항 형식

함수에 인수를 1개 넘기는 경우는 보통 2가지다.
하나는 인수에 질문을 던지는 경우이다.

boolean fileExists("Myfile")

다른 하나는 인수를 뭔가로 변환해 결과를 반환하는 경우이다.

InputStream fileOpen("Myfile")
String => InputStream

함수 이름을 지을 때는 위 두가지 경우를 분명히 구분하라.
또한 언제나 일관적으로 두 형식을 사용하라.

10. 플래그 인수

함수로 부울 값을 넘기는 관례는 정말 끔찍하다.
이는 함수가 여러가지를 처리한다고 공표하는 것이나 마찬가지이다.

11. 인수 객체

인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성일 짚어본다.
예를 들면,

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);

12. 인수 목록

때로는 인수 개수가 가변적인 함수도 필요하다.

13. 동사와 키워드

단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야한다.
예를 들어, write(name) 과 같다.
좀 더 나은 이름은 writeField(name) 이다.
이렇게 하면 이름(name)이 필드(Field)라는 것이 분명해진다.

만약 함수 이름에 인수이름을 넣는다면, 인수 순서를 기억할 필요가 없어진다.
assertExpectedEqualsActual(expected, actual)과 같이 말이다.

14. 출력 인수

출력 인수는 가능한 피하라. 출력 인수는 인지적으로 거슬린다.
한번에 이해하기 어렵다는 말이다.
*출력 인수: 인수로 목적이 되는 객체를 사용하는 것.

15. 명령과 조회를 분리하라!

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다.
객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.

16. 오류 코드보다 예외를 사용하라!

오류 코드로 오류 발생을 탐지하지 말고,
예외처리 블록을 사용하라.

try/catch 블록은 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.
그러므로 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.
예외 처리 함수는 예외만 처리하고,
실제 동작하는 함수는 동작만 수행하게 하라.

17. 반복하지 마라!

코드에서 가능한한 중복을 없애라.
객체지향 프로그래밍에서는 코드를 부모 클래스로 몰아 중복을 없앤다.

결론

대가(master) 프로그래머는 시스템을 (구현할)프로그램이 아니라 (풀어갈)이야기로 여긴다.
프로그래밍이 언어라는 수단을 사용해 좀 더 풍부하고 좀 더 표현력이 강한 언어를 만들어 이야기를 풀어간다.
여러분이 작성하는 함수가 분명하고 정확한 언어로 깔끔하게 같이 맞아 떨어져야 이야기를 풀어가기 쉬워진다는 사실을 기억하기 바란다.