클린코드 3장 함수 요약
·4 mins
작게 만들어라. #
- 함수를 만드는 첫째 규칙은 ‘작게’이고, 둘째도 ‘작게’다.
- 켄트 백의 프로그램 Sparkle은 모든 함수가 2~4줄 정도였고, 각 함수가 너무도 명백했다.
블록과 들여쓰기 #
- if / else문, while문 등에 들어가는 블록은 한줄이어야 한다.
- 대게 거기서 함수를 호출한다.
- 이 말은 즉, 중첩 구조가 생길만큼 함수가 커져서는 안된다는 의미이다.
한 가지만 해라! #
- 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야한다.
- ‘한 가지’란? 추상적인 수준의 하나의 작업을 의미한다.
- 예를 들어 ‘물마시기’는 컵을 들고 → 입에 가져다 대고 → 마신다 라는 과정을 거치지만 ‘물을 마신다’라는 추상적 개념으로 하나의 작업으로 볼 수 있음.
- 우리가 함수를 만드는 이유는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서이다.
- 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
함수당 추상화 수준은 하나로! #
- 함수가 확실히 ‘한 가지’ 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
- 한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다.
- 특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어렵기 때문이다.
- 근본 개념과 세부사항을 뒤섞기 시작하면, 깨어진 창문처럼 사람들이 함수에 세부사항을 점점 더 추가한다.
- 내려가기 규칙
- 코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
- 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
- 즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
switch 문 #
- 본질적으로 switch 문은 N가지를 처리한다.
- 각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법이 있다. 다형성을 이용하자.
- switch문을 추상 팩토리에 숨기고, 파생 클래스가 함수를 실행하도록 하자.
서술적인 이름을 사용하라! #
- 워드가 말했던 클린 코드의 원칙, “코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 클린 코드라 불러도 된다.”
- 한 가지만 하는 작은 함수에 좋은 이름을 붙인다면 이 원칙을 달성함에 있어 이미 절반은 성공한다.
- 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
- 이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
- 서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기 쉬워진다.
함수 인수 #
- 이상적인 인수는 0개다. 다음은 1개이고, 3개 이상은 가능한 피하자.
- 테스트 관점에서 보면 인수는 어렵다.
- 갖가지 인수 조합으로 함수를 검증하는 테스트 케이스를 작성한다고 상상해보자.
- 인수가 0개이면 간단하다. 3개이상이면 상당히 부담스럽다.
- 출력 인수는 입력 인수보다 이해하기 어렵다.
- 흔히 우리는 함수에다 인수로 입력을 넘기고 반환값으로 출력을 받는다는 개념에 익숙하다.
- 대개 함수에서 인수로 결과를 받으리라 기대하지 않는다.
- 단항 형식
- 함수에 인수를 1개 넘기는 이유로 가장 흔한 경우 두가지이다.
- 인수에 질문을 던지는 경우
- 인수를 뭔가로 변환해 결과를 반환하는 경우
- 드물지만 단한 함수 형식 이벤트도 사용함.
- 함수에 인수를 1개 넘기는 이유로 가장 흔한 경우 두가지이다.
- 플래그 인수
- 플래그 인수는 끔찍하다.
- 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈이나 마찬가지이다.
- 이항 함수
- 인수가 2개이면 1개인 경우보다 이해하기 어렵다.
- new Point(x, y)와 같은 경우는 적절하다. x, y는 자연적인 순서를 가진다.
- 하지만 다른 경우 인수간에 자연적 순서가 없이, 인위적인 순서를 기억해야 하는 불편함을 가진다.
- 이항 함수는 프로그램을 짜다보면 불가피한 경우도 생기지만, 가능하다면 단항함수로 바꾸도록 노력하자.
- 인수 객체
- makeCircle(x, y, radius)를 인수 객체를 사용해 makeCircle(point, radius)로 고칠 수 있다.
사이드 이펙트를 일으키지마라 #
- 사이드 이펙트는 거짓말이다. 함수에서 한 가지를 하겠다고 약속하고선 남몰래 다른 짓을 하니까.
- 사이드 이펙트는 ‘시간적인 결합’이나 ‘순서 종속성’을 초래할 수 있다.
명령과 조회를 분리하라! #
- 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
- 둘 다 하면 혼란을 초래한다.
오류 코드보다 예외를 사용하자 #
if (deletePage(page)) === E_OK)
- 명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다.
- 자칫하면 if문에서 명령을 표현식으로 사용하기 쉬운 탓이다. ??
- 위 코드는 여러 단계로 중첩되는 코드를 야기한다.
- 오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 한다는 문제에 부딪힌다.
- try / catch 블록은 추하다.
- 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.
- 그러므로 try / catch 블록을 별도 함수로 뽑아내는 편이 좋다.
- 오류 처리도 한 가지 작업이다.
- 함수는 ‘한 가지’ 작업만 해야 한다. 오류처리도 작업에 속한다.
- 오류를 처리하는 함수는 오류만 처리해야 마땅하다.
반복하지 마라! #
- 중복은 문제다.
- 코드 길이가 늘어날 뿐 아니라 알고리즘이 변하면 네 곳이나 손봐야 한다.
함수를 어떻게 짜죠? #
- 소프트웨어를 짜는 행위는 여느 글짓기와 비슷하다.
- 논문이나 기사를 작성할 때는 먼저 생각을 기록한 후 읽기 좋게 다듬는다.
- 함수도 마찬가지로 처음에는 길고 복잡하다. 들여쓰기 단계도 많고 중복된 루프도 많다. 인수 목록도 아주 길다. 이름은 즉흥적이고 코드는 중복된다. 그 코드를 테스트하는 단위테스트 코드를 만들고 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거해야 한다.
- 최종적으로 이 장에서 설명한 규칙을 따르는 함수가 얻어진다. 처음부터 짜내는건 어렵다.
결론 #
- 함수는 언어에서 동사며, 클래스는 명사다.
- 마스터 프로그래머는 시스템을 구현할 프로그램이 아니라 풀어갈 이야기로 여긴다.
- 이 장에서 함수를 잘 만드는 기교를 소개했지만, 진짜 목표는 시스템이라는 이야기를 잘 풀어나가는 데 있다는 사실을 명심하자.