함수는 프로그램의 기본적인 단위이다.
함수를 잘 만드는 방법에 대해 알아보자.
함수는 작게 만들어야 한다.
각 함수는 이야기 하나를 표현해야한다.
블록 안에서 호출하는 함수 이름을 적절하게 짓는다면 코드를 이해하기도 쉬워진다.
중첩 구조가 생길만큼 함수가 커져서는 안된다.
함수는 한 가지를 해야한다.
그 한 가지를 잘 해야 한다.
그 한 가지만을 해야한다.
지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 것이다.
한가지 작업만 하는 함수는 자연스럽게 섹션으로 나누기 어렵다.
함수 당 추상화 수준은 하나여야 한다.
함수가 확실히 한가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다 (근본 개념과 세부사항이 뒤섞여 구분이 어려워짐).
코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
가급적이면 switch 문은 사용하지 말자.
switch 문은 작게 만들기 어렵다.
한 가지만 작업하는 switch 문도 만들기 어렵고, 본질적으로 switch문은 N가지를 처리한다.
switch 문을 사용해야한다면, 저차원 클래스에 숨기고 반복하지 않는 방법이 있다.
서술적인 이름을 사용해야 한다.
함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
길고 서술적인 이름이 길고 서술적인 주석보다 좋다.
함수 이름을 정할 때는 여러 단어가 쉽게 읽히는 명명법을 사용한다.
서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기 쉬워진다.
이름을 붙일 때는 일관성이 있어야 한다.
함수에서 이상적인 인수 개수는 0개이다. 다음은 1개, 2개이다.
함수에서 3개의 인수는 가능한 피하는 편이 좋으며, 4개 이상은 특별한 이유가 필요하다.
인수가 3개를 넘어가면 인수마다 유효한 값으로 모든 조합을 구성해 테스트하기가 상당히 부담스러워진다.
인수 객체 - 인수가 2~3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 고려해야한다.
인수 목록 - 가변 인수를 취하는 함수는 단항, 이항, 삼항 함수로 취급할 수 있다.
인수에 질문을 던지는 경우
인수를 변환해 결과를 반환하는 경우
이벤트 함수
입력 인수만 존재하고 출력 인수는 없음
시스템 상태를 바꿈
이벤트라는 사실이 코드에 명확히 드러나야 함
입력 인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려준다.
함수가 한번에 여러가지 일을 처리하는 것일 수 있다.
플래그 인수는 좋지 못하다.
인수가 2개인 함수는 인수가 1개인 함수보다 이해하기 어렵다.
이항 함수가 적절한 경우도 있다.
좌표와 같이 인수 2개가 한 값을 표현하는 경우가 그 예이다.
이항 함수가 무조건 나쁜 것은 아니지만 가급적 단항 함수로 바꾸도록 노력하는 것이 좋다.
인수가 3개인 함수는 인수가 2개인 함수보다 더 이해하기 어렵다.
함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름이 필수적이다.
함수 이름에 키워드를 추가하는 형식을 사용해볼 수 있다.
ex) assertExpectedEqualsActual(expected, actual)
부수 효과를 일으키지 말아야 한다.
많은 경우 시간적인 결합(temporal coupling)이나 순서 종속성(order dependecy)을 초래한다.
만약 시간적인 결합이 필요하다면 함수 이름에 분명히 명시한다.
일반적으로 출력 인수는 피해야한다.
함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택한다.
명령과 조회를 분리하라
함수는 뭔가를 수행하거나 뭔가에 답하거나 하나만 해야 한다.
객체 상태를 변경하거나 객체 정보를 반환하거나 하나만 해야 한다.
오류 코드보다 예외를 사용하라
명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 위반한다.
오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.
try/catch
블록은 정상 동작과 오류 처리 동작을 뒤섞을 수 있기 때문에 블록을 별도 함수로 작성하는 것이 좋다.
정상 동작과 오류 처리 동작을 분리하면 코드를 이해하고 수정하기 쉬워진다.
단일 입/출구 규칙
모든 함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야 한다.
함수는 return
문이 하나여야 한다.
루프 안에서 break
, continue
를 사용해서는 안 되며 goto는 절대로 사용하지 않는다.
하지만 함수를 작게 만든다면 return
, break
, continue
를 여러 차례 사용해도 괜찮다.
클린 코드는 여러번 봐도 반성하게 되는 것 같습니다
잘 보고 갑니다