Community

개발자 99% 커뮤니티에서 수다 떨어요!

← Go back
TIL 3장 함수
#clean_code
2년 전
608


TIL (Today I Learned)

  • 2022.02.22

오늘 읽은 범위

  • 3장. 함수

책에서 기억하고 싶은 내용을 써보세요.

  • 어떤 프로그램이든 가장 기본적인 단위가 함수다.

  • 작게 만들어라!

  • 한 가지만 해라!

- 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.

- 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.

- 따라서, 함수가 '한 가지'만 하는지 판단하는 방법이 하나 더 있다. 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.

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

- 함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일 해야 한다.

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

- 코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다. 즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다. 나는 이것을 내려가기 규칙이라 부른다.

  • Switch 문

- 상속 관계로 Switch 문을 숨긴 후에는 절대로 다른 코드에 노출하지 않는다.

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

- 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.

- 서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기 쉬워진다. 좋은 이름을 고른 후 코드를 더 좋게 재구성하는 사례도 없지 않다.

- 이름을 붙일 때는 일관성이 있어야 한다. 모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다.

  • 함수 인수

- 함수에서 이상적은 인수 개수는 0개다.

- 인스턴스 변수로 선언하는 대신 함수 인수로 넘기는 방법도 있었다. 하지만 그랬다면 코드를 읽는 사람이 StringBuffer를 발견 할 때마다 의미를 해석해야 한다.

- 코드를 읽는 사람이 StringBuffer를 발견할 때마다 의미를 해석해야한다.(안좋음)

- 코드를 읽는 사람이 현 시점에서 별로 중요하지 않은 세부사항, 즉 StringBuffer를 알아야 한다.(안좋음)

  • 많이 쓰는 단항 형식

- 입력 인수를 변환하는 함수라면 변환결과는 반환값으로 돌려준다.

  • 플래그 인수

- 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈

  • 동사와 키워드

- 함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름이 필수다.

- 함수 이름에 키워드를 추가하는 형식이다. 즉, 함수 이름에 인수 이름을 넣는다. 예를 들어, assertEquals보다 assertExpectedEqualsActual(expected, actual)이 더 좋다. 그러면 인수 순서를 기억할 필요가 없어진다.

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

- 함수 이름만 보고 함수를 호출하는 사용자는 사용자를 인증하면서 기존 세션정보를 지워버릴 위험에 처한다.

- 이런 부수효과가 시간적인 결합을 초래한다. 즉, checkPassword 함수는 특정 상황에서만 호출이 가능하다. 다시 말해, 세션을 초기화해도 괜찮은 경우에서만 호출이 가능하다.

  • 출력 인수

- 인수 s가 출력 인수라는 사실은 분명하지만 함수 선언부를 찾아보고 나서야 알았다. 함수 선언부를 찾아보는 행위는 코드를 보다가 주춤하는 행위와 동급이다. 인지적으로 거슬린다는 뜻이므로 피해야 한다.

  • 명령과 조회를 분리하라!

- 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다. 둘 다 하면 안된다.

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

- 오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.

  • Try/Catch 블록 뽑아내기

- try/catch블록은 원래 추하다. 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.

  • 오류 처리도 한 가지 작업이다.

- 함수는 '한 가지'작업만 해야 한다. 오류 처리도 '한 가지' 작업에 속한다.

- 오류 코드 대신 예외를 사용하면 새 예외는 Exception 클래스에서 파생된다. 따라서 재컴파일/재배치 없이도 새 예외 클래스를 추가할 수 있다.

  • 반복하지 마라!

- 중복은 문제다.

- include 방법으로 중복을 없앤다. 코드를 다시 한 번 읽어본다. 중복을 없앴더니 모듈 가독성이 크게 높아졌다

  • 구조적 프로그래밍

- 모든 함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야 한다고 말했다. 즉, 함수는 return 문이 하나여야 한다는 말이다. 루프 안에서 break나 continue를 사용해선 안 되며 goto는 절대로, 안된다.

- 구조적 프로그래밍의 목표와 규율은 공감하지만 함수가 작다면 위 규칙은 별 이익을 제공하지 못한다. 함수가 아주 클때만 상당한 이익을 제공한다.

- 그러므로 함수를 작게 만든다면 간혹 return, break, continue를 여러 차례 사용해도 괜찮다. 오히려 때로는 단일 입/출구 규칙보다 의도를 표현하기 쉬워진다. 반면, goto 문은 큰 함수에서만 의미가 있으므로, 작은 함수에서는 피해야함 ㄴ한다.

  • 함수를 어떻게 짜죠?

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

  • 결론

- 함수는 그 언어에서 동사며, 클래스는 명사다.

- 프로그래밍의 기술은 언제나 언어 설계의 기술이다.

- 대가 프로그래머는 시스템을 구현할 프로그램이 아니라 풀어갈 이야기로 여긴다.

오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요

  • 나는 여태 함수를 만들 때 인수를 사용했다. 왜냐하면 구현할 때 깔끔했기 때문이다. 그런데 아차 싶었다. 만약 다른사람이 내 함수를 본다면 무슨 인수인지 몰라 그 함수를 호출하는 곳을 찾고, 또 찾고 했을것이다. 시간을 많이 허비했을 듯 싶다.

  • 나 또한 기존의 함수들을 살펴볼때 다 IDE에서 quick search를 이용해서 찾아 찾아 함수를 해석했다. 함수에서 한가지만 하지 않고 여러가지 함수를 동시 다발적으로 쓰고 50줄짜리 함수도 만들어 봤다. 반성이 된다.

궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.

  • 그래도... 인수는 쓰라고 있는 거 아닐까요?? 인수를 전혀 쓰지 않는다면 선언하는 변수가 너무 많아질거 같아요 ㅠㅠ 출력값을 인수값으로 가져오는건 좀 아니라고 생각이 들지만 수학적으로 함수는 입력값이 있고, 출력값이 있는데 프로그래밍도 너무 인수값을 안쓰려고 하면 힘들듯 ㅠㅠㅠ