본문 바로가기
프로젝트/클린코드 노마드 북클럽

북클럽 4일차. 3장 함수

by 1005ptr 2024. 6. 26.
반응형

읽기 쉽고 이해하기 쉬운 함수는?
의도를 분명히 표현하는 함수를 어떻게 구현할 수 있을까?
함수에 어떤 속성을 부여해야 처음 읽는 사람이 프로그램 내부를 직관적으로 파악할 수 있을까?

작게 만들어라!

  • 가로 150자를 넘어서는 안된다.
  • 함수는 100줄을 넘어서는 안된다. 아니 20줄도 길다.
  • 켄트 벡의 Sparkle 예시
    • 모든 함수가 2줄, 3줄, 4줄 정도였다.
    • 각 함수가 너무도 명백했다.
    • 각 함수가 이야기 하나를 표현했다.
    • 각 함수가 너무도 멋지게 다음 무대를 준비했다.
  • 조건문 반복문 안에서 주절주절 하지 말고 함수로 뺴라.
    • if문, else문, while문 등에 들어가는 블록은 한줄이어야 한다.
    • 중첩 구조가 생길만큼 함수가 커져서는 안된다.
    • 함수에서 들여쓰기 수준은 1단이나 2단을 넘어서면 안된다.

한 가지만 해라!

  • 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
  • 그 한가지가 무엇인지 알기가 어렵다.
    • 지정돤 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
    • 우리가 함수를 만드는 이유는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서 이다.
    • 함수가 1이면 1-1 단계만 있어야 좋다. 1-1-1이 있으면 별로다.
  • 함수가 한가지만 하는지 판단하는 방법 두번째
    • 단순히 다른 표현이 아니라 의미있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
  • 함수 내 섹션이 나눠지면 여러 작업을 한다는 뜻
    • declarations
    • initializations
    • sieve
    • 코드 단계가 구분이 되면
    • 여기서 섹션은 함수가 아니라 코드 섹션을 말함

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

  • 함수가 한가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
  • 수준이 섞이면 코드를 읽을때 헷갈린다.

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

  • 코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
  • 추상화 수준이 높은 함수가 맨 위에 있고 추상화 수준에 따라 함수를 정렬해서 두자.
  • 한 클래스 내에서 추상화 수준이 깊어지면 클래스를 쪼개는것도 좋을듯?
  • 단순히 생각해보면 public이 1단계, private이 2단계, 3단계는 안만드는게 좋을듯

Switch 문

  • switch의 문제
    • 작게 만들기 어렵다. ==> 길게 적으면 알아보기 어렵기가 십상이다.
    • 단일 블록이나 함수를 적는게 가독성 문제가 없다.
    • 한가지 작업만 하는 swtich 문도 만들기 어렵다.
    • 본질적으로 switch 문은 N가지를 처리한다.
    • switch 문을 완전히 피할 방법은 없다.
  • 하지만 switch문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다.
    • 다형성을 이용
    • 책임이 하나가 아니면 코드를 변경할 이유가 여럿이 된다.
    • 파생 클래스에 switch가 있긴 함.
    • 상속 관계로 숨긴 후에는 switch를 다른 코드에 노출하지 않는다.

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

  • 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
  • 이름이 길어도 괜찮다.
  • 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
  • 길고 서술적인 이름이 길고 서술적인 주석보다 좋다.
  • 이름은 길면서도 잘 읽혀야 한다.
  • 문체를 일관성있게 짓자.
  • 문체가 비슷하면 이야기를 순차적으로 풀어가기도 쉬워진다.

함수 인수

  • 함수에서 이상적인 인수는 0개다
  • 3개 부터는 가능한 피하는 편이 좋다.
  • 4개 이상은 특별한 이유가 필요하며 특별한 이유가 있어도 사용하면 안된다.
  • 함수 이름과 인수 사이에 추상화 수준이 같아야 한다.
  • 테스트할때도 인수가 없는게 훨씬 편하다.
  • 플래그 인수
    • 불린 값은 끔찍하다.
    • 한꺼번에 여러가지를 처리한다고 대놓고 공표하는 셈이다.
    • 함수 쪼개는게 맞다.
  • 이항 함수
    • 대표적 사례인 assertEquals(expected, actual)도 오용 사례가 많다.
    • 두개 순서를 바꿔 넣어서 오동작하는 경우 많음.
  • 삼항 함수
    • 훨씬 더 어렵다..
    • 자연스러울때도 제거할 방법을 찾아보자.
  • 인수 객체
    • 인수를 독자적인 클래스 변수로 묶을 수 있다.
    • 눈속임이라고 볼 수도 있지만 개념으로 묶이는 효과가 있다.
  • 인수 목록
    • 인수 개수가 가변적인 경우
      • String.format 같은 함수
      • Spread(...) 문법 같은거 말함
  • 동사와 키워드
    • 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다. writeField(name)

부수 효과를 일으키지 마라!

  • 뭘 하겠따고 해놓고 몰래 다른일도 하는건 완전 나쁜짓이다.
  • 많은 경우 시간적인 결합(temporal coupling)이나 순서 종속성(order dependency)를 초래한다.

명령과 조회를 분리하라!

  • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
  • 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.
  • if(set("username", "unclebob"))
    • 이거 별로다.
    • 조건문으로 쓰이는데 입력도 한다. 이렇게 쓰면 안된다.
    • 조건문에 쓸거면 boolean 변수에 담아서 해야 된다.

오류 코드보다 예외를 사용해라!

  • 명령 함수에서 오류 코드를 반환하는 것 보다 예외를 반환하는게 좋다.
  • 오류 코드를 반환하면 호출자가 어떻게든 해당 값을 처리해야된다.
  • 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 깔끔해진다.
  • 오류 처리도 한 가지 작업이다.
    • 함수에 try가 있으면 같은 레벨에 catch, finally만 있어야 한다.
    • 그 밖에 뭐가 또 있고 이러면 안된다.

반복하지 마라!

  • 이것저것 코드가 조금씩 달라져도 본질이 같은 경우가 있따. 중복은 제거하는게 맞다.
  • 중복은 소프트웨어에서 모든 악의 근원이다.

구조적 프로그래밍

  • 큰함수에서는 return, break, continue 등을 절제 하는게 도움이 된다.
  • 작은 함수에서는 크게 의미는 없는 규칙이다.

함수를 어떻게 짜죠?

  • 처음엔
    • 길고 복잡하다.
    • 들여쓰기 단계도 많고 중복된 루프도 많다.
    • 인수 목록도 아주 길다.
    • 이름은 즉흥적이고 코든느 중복된다.
    • 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스도 만든다.
  • 그 다음
    • 코드를 다듬고, 함수를 만들고(뽑고), 이름을 바꾸고, 중복을 제거한다.
    • 메서드를 줄이고 순서를 바꾼다.
    • 떄로는 전체 클래스를 쪼개기도 한다.
    • 이 와중에도 코드는 항상 단위 테스트를 통과한다.

결론

  • 모든 프로그램은 해당 도메인 내에서 이름이 명명된다.
  • 클래스는 명사다.
  • 함수는 동사다.
  • 진짜 목표는 시스템이라는 이야기를 풀어가는 데 있다는 사실을 명심해야 한다.
  • 작성하는 함수가 분명하고 정확한 언어로 깔끔하게 같이 맞아떨어져야 이야기를 풀어가기가 쉬워진다.

참고 도서

  • 소프트웨어 개발의 지혜: 원칙, 디자인패턴, 실천방법
  • GOF의 디자인 패턴(개정판)
  • 실용주의 프로그래머
반응형

댓글