[TIL] 20230716: 클린코드 3장 - 함수

ERyukSa·2023년 7월 16일
0

TIL

목록 보기
3/6

클린코드 3장: 함수

주제

프로그램의 기본 단위는 함수다. 함수 잘 만드는 법을 알아보자.

내용

0. 대원칙

  1. 코드는 이야기처럼 읽히는 것이 좋다.
  2. 인지적으로 거슬리는 행위를 피한다
    -> '코드를 다시 읽어보도록 만든다, 읽다가 주춤하게 만든다, 호출부만으론 부족해서 함수 선언부나 본문을 찾아보게 만든다'를 피한다
  3. 반복하지 않는다(DRY)

1. 작게 만들어라!

첫 째 규칙은 작게, 둘 째 규칙은 더 작게 만들라는 것이다.
중첩 구조가 생기지 않을 만큼 작게 만들어라. 조건문/반복문으로 중첩구조가 생긴다면 블록 안에 들어가는 문장은 한 줄이어야 한다.
보통 함수를 호출한다. 중첩은 1~2단이면 충분하다.

2. 한 가지만 하라

한가지라는 의미는 주관적이다.
함수의 이름이 의미하는 작업들만 수행한다면 함수에 몇 가지 절차가 있더라도 한 가지 일만 하는 것이다.
--> 그럼 함수 이름을 어떻게 짓느냐가 문제가 될 것이다.

지정 작업 외의 의미를 갖는 함수를 추출할 수 있다면 여러 작업을 하고 있는 것이다.
ex) 객체에 어떤 기능을 추가해서 리턴하는 이름의 함수가 그 기능을 생성하는 다소 긴 절차를 포함하고 있다면, 기능 생성이라는 또 다른 절차를 포함하고 있으므로 여러 작업을 하는 것이다.

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

하나의 함수에서 추상화 수준을 통일하라.

추상화 수준이란 다음과 같다
1. getHtml() -> 2. PathParser.render(pagePath) -> 3. ~.append("\n")

함수 안으로 들어갈수록 추상화 수준이 낮아져야 한다. 마치 목차 안에 세부 목차가 있고, 그 안에 다시 세부 목차가 있는 것처럼.

4. Switch문

Switch문은 본질적으로 여러가지 처리를 하기 때문에 길다. 하지만 다형성을 이용하면 switch문을 숨겨서 다시 반복하지 않을 수 있다.
하위 클래스 타입에 따라 분기하는 switch문은 다형성으로 해결한다.
하위 타입 객체 생성에만 switch문을 사용하고, 나머지 메서드는 인터페이스를 거쳐 호출하도록 한다.

5. 길고 서술적인 이름이 짧고 어려운 이름보다 낫다. 여러 이름을 붙여봐도 좋다. 오래 걸려도 괜찮다.

6. 함수 인자는 함수를 이해하기 어렵게 만든다. 호출부를 읽을 때마다 인자를 이해해야 하기 때문이다.

테스트를 할 때도 인자가 많을수록 유효한 인자 조합을 생성해야 하므로 어렵다.

인자에 결과를 입력받는 구조는 코드를 다시 읽게 만든다.
ex) void setPageInto(StringBuffer pageText)

최선은 인자 0개, 차선은 1개

7. 플래그 인자는 추하다. 한 번에 두 가지 작업을 하기 때문이다. true인 경우와 false 경우의 함수를 분리하는 편이 낫다.

8. 이항 함수

인자 1개로 개선할 수 있다면 하는 게 좋다. 예외는 좌표계처럼 인자가 반드시 함께 있어야 하나를 표현하는 경우이다.

함수(A, B)보다 A.함수(B)가 낫다. 전자는 첫 인수를 무시해도 된다는 것을 깨닫는 시간이 필요하다. 그리고 무언가를 무시하는 행위는 실수를 유발하므로 위험하기도 하다.

9. 인자 줄이는 방법

1) 인자 대신 인스턴스 변수
2) 함수를 클래스의 멤버로 이동
3) 인자 일부를 하나의 객체로 묶는다
4) 가변 인자
5) 새로운 동작 클래스 정의, 생성자에서 인자 중 일부를 받는다.

10. 함수 이름

단항 함수는 동사와 명사가 쌍을 이루는 게 좋다.
이항 함수는 함수명에 인자 정보를 넣어 순서를 명확히 할 수 있다. ex)assertExpectedEqualsActual

11. 부수효과를 일으키지 마라

함수 이름을 보고 예상할 수 없는 작업을 함수에 포함시키지 마라.

12. 명령과 조회를 분리하라

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

둘 다 하면 혼란을 초래한다. 분명 명령형 함수인데 조건문 괄호 안에 있으면 명령문인지 조회문인지 헷갈린다.

13. 결과 코드 대신 예외를 사용하자.

작업의 성공 여부를 반환하고 싶다면 예외를 사용하라. 작업 코드 또는 성공 여부를 반환하면 바깥에서 중첩문을 만들게된다.

예외를 발생시키면 바깥에서는 성공문과 예외문이 분리되어 깔끔해진다.
성공문과 예외문을 별도의 함수로 분리하자. 그럼 두 프로세스를 포괄하는 함수도 이해하기 쉬워진다.

14. 함수 짜는 전략

프로그래밍은 글짓기와 비슷하다.

처음에 짠 함수는 어설프다. 길고, 복잡하고, 중첩이 많고, 인자도 많고, 이름은 즉흥적이고, 중복된 코드가 있다.

하지만 그 서투른 코드의 단위 테스트 케이스를 만든다. 그리고 다듬는다.
그리고 매번 다듬으며 단위 테스트를 통과시킨다. 그러면 짧고, 읽기 좋고, 분명하고, 중복 없는 함수가 만들어진다.

처음부터 마법처럼 뚝딱 완성되지 않는다.

프로그래밍은 구현이 아니라 이야기를 써내려가는 것이다. 술술 읽힐 수 있도록 노력하자.

질문

의문)
12번 정리에서 결과 코드 보다 예외를 사용하라는 이점이 공감되지 않는다. try/catch도 바깥에서 중첩을 만드는 건 같지 않나.

작업 코드 역시 성공과 나머지 케이스로 나눌 수 있다. 나머지 케이스가 여럿일 수 있지만 예외의 종류도 여러가지가 될 수 있다.

뒤에 내용이 더 있는 것 같으니, 마저 읽어보자. (p.59)

해결) 오류 코드에 변경이 생기면 리컴파일을 해야 한다.(다른데서 임포트 하고 있으므로) 반면, 예외는 새로 추가하더라도 Exception의 하위 클래스이므로 리컴파일 하지 않아도 된다.

느낀점

읽다 보면 인문학 책 같다는 느낌을 받는다. 재밌으면서도 어렵다.

단순히 요구 사항을 구현하는 것이 아니라 이야기를 써내려간다는 생각으로 임한다는 것이 인상 깊었다. 나도 이런 자세로 한다면 더 즐길 수 있을까?

위로(?)도 받았다. 좋은 함수는 한 번에 뚝딱 만들어지지 않는다고, 저명한 저자조차 처음엔 너저분한 함수에서 시작한다고.

평소에 여기서 하지 말란 대로 한 게 많았다..ㅋㅋ 책 내용이 무조건 정답은 아니겠지만 여러 사람이 추천하는 이유가 있을테니 적용해봐야지. 밥 아저씨 믿습니다!!

0개의 댓글