함수를 잘 만드는 법을 공부한다.
함수를 만드는 첫째 규칙은 '작게'고 둘째 규칙도 '더 작게'다.
블록과 들여쓰기
다시 말해, if문 else문 등에 들어가는 블록은 한 줄이어야 한다.
그러면 바깥을 감싸는 함수도 작아지고, 코드를 이해하기도 쉬워진다.
함수는 한 가지를 해야하고, 그 한 가지를 잘 해야 하고, 그 한 가지만을 해야한다.
한 가지라는 것은 추상화 수준을 하나만 수행하라는 것이다.
한 가지 작업만 하는 함수는 자연스럽게 여러 섹션으로 나누기 어렵다.
함수가 한 가지만 하려면 함수의 모든 문장의 추상화 수준이 동일해야 한다.
추상화 수준을 섞으면 특정 표현이 근본 개념인지 세부사항인지 헷갈리게 된다.
내려가기 규칙
코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
위에서 아래로 프로그램을 읽어가며(자연스레 맥락이 읽히며) 함수 추상화 수준이 한 번에 한 단계씩 낮아지게 하자.
쉽지 않지만 매우 중요한 규칙이다.
코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 된다.
좋은 이름이 주는 가치는 아무리 강조해도 지나치지 않다.
이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
또한, 일관성을 유지해라.
인수가 들어가면 해석이 어려워진다.
/* 함수 이름과 인수 사이에 추상화 수준이 다를뿐더러,
현 시점에서 중요하지 않은 세부사항(PageContent)를 알아야 한다. */
includeSetupPageInto(new PageContent)
// 이해하기 더 쉽다.
includeSetupPage()
함수에서 이상적인 인수 개수는 0개이다. 3개부턴 가능하면 피하는 것이 좋다.
최선은 입력 인수가 없는 것이며, 차선은 입력 인수가 1개뿐인 것이다.
플래그 인수
플래그는 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 하는 셈이다. nope!
이항 함수
프로그램을 짜다보면 불가피하게 이항 함수가 필요한 경우도 있겠지만, 가능하면 단항 함수로 바꾸도록 애써야 한다.
인수 객체
인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 수도 있다.
Circle makeCircle(double x, double y, double radius);
// 단순 눈속임이 아닌 개념을 표현하게 된다.
Circle makeCircle(Point center, double radius);
출력 인수
인수는 함수 입력으로 해석된다. 인수를 출력으로 사용하지 말자. (객체지향 언어에서 this로 출력 인수 사용가능)
appendFooter(report); // 출력 인수를 피하자
report.appendFooter(); // 함수 내에서 this를 사용해서 객체 상태 변경
동사와 키워드
함수와 인수가 동사/명사 쌍을 이루면 이해하기 쉽다.
함수 이름에 키워드(인수 이름)을 넣는 방법도 있다.
writeField(name) // 동사/명사 쌍으로 곧바로 이해됨.
assertEquals(expected, actual) // 인수의 순서가 확실치 않다.
assertExpectedEqualsActual(expected, actual) // 키워드 추가함으로써 순서 명시됨
한 가지만 하기로 약속하고선, 다른 짓도 시키지 말자.
함수는 뭔가를 수행하거나 뭔가를 답하거나 하나만 해야 한다.
둘 다 하면 코드를 읽을때 혼란을 초래한다.
오류 코드 대신 예외(try/catch)를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.
그리고, try/catch 블록은 별도 함수로 뽑아내는 편이 깔끔해진다. (정상 동작과 오류 처리 동작 분리)
중복되면 겹치는 모든 곳을 손봐야 한다. include 방법으로 중복을 없애자.
함수는 return 문이 하나여야 하며, 루프 안에서 break나 continue를 사용해선 안된다.
goto는 절대절대 안된다.
초안은 대게 서투르고 어수선하지만, 원하는 대로 읽힐 때까지 다듬는다.
끊임없이 고쳐가되, 항상 단위 테스트 케이스를 통과해야 한다.
함수를 잘 만드는 기교를 배웠다.
좀 더 풍부하고 좀 더 표현력 강하게 코드를 짜보자!
Reference
클린 코드: 애자일 소프트웨어 장인 정신 - 로버트 마틴 지음