클린코드 - 03. 함수

 

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

우리는 어떤 함수를 읽었을때 프로그램 내부를 직관적으로 파악할 수 있을까?

 

작게 만들어라

  • 블록과 들여쓰기
    • 중첩 구조가 생길 만큼 함수가 커지면 안된다. (if else문 등 주의하여 쓰자)
    • 블록 안에서 호출하는 함수 이름을 적절히 짓는다면 코드를 이해하기 쉽다.
  • 한가지만 해라
    • 예시: 1. 페이지가 테스트 페이지인지 판단 => 2. 설정 페이지와 해제 페이지를 넣는다 => 3. 페이지를 HTML로 렌더링한다.
      • 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한가지 작업을 한다고 할 수 있다.
      • 더이상 줄이기가 불가능 하며 if, else를 따로 뺀다고 해도 다른 표현일 뿐 추상화 수준이 바뀌지 않는다.
  • 함수 당 추상화 수준은 하나로!
    • 함수가 확실히 '한가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야한다.
    • 수준이 섞여있을 경우 근본 개념인지 세부사항인지 구분하기 어려워져 추후에 사람들이 함수에 세부사항을 점점 더 추가하게 된다.
  • 위에서 아래로 코드 읽기 (내려가기 규칙)
    • 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
    • 즉 위에서 아래로 읽으면 함수 추상화 수준이 한 단계씩 낮아진다.

 

Switch문

  • 다형성을 이용한다.
  • 상속관계를 숨긴 후 절대로 다른 코드에 노출하지 않는다.

 

서술적인 이름을 사용하라

  • 길고 서술적인 이름이 짧고 이해하기 어려운 이름보다 좋다. (일관성있게!)

 

함수 인수

  • 이상적인 인수 개수는 0개 (무항)
    • 4개 이상은 특별한 이유가 필요하다. (이유가 있어도 사용해서는 안된다..)
  • 플래그 인수
    • 함수 안에 여러가지 일을 처리하는 것을 의미한다.
    • 함수로 부울 값을 넘기는 것은 좋지않다.
      • render(boolean isSuite) => renderForSuite(), renderForSingleTest() 로 함수를 나누는 것이 좋다.
  • 이항, 삼항 함수
    • 인수가 많은 함수는 적은 함수보다 이해하기 어렵다.
    • 단항에 비해 위험이 따른다.
      • 순서, 무시로 야기되는 문제가 생길 수도 있다.
  • 동사와 키워드
    • 함수 이름에 인수의 순서, 의도를 넣는것도 좋다.
      • assertEqual(expected, actual) => assertExpectedEqualsActual(expected, actual) 이렇게 할 경우 인수의 순서를 기억할 필요가 없다.

 

부수 효과를 일으키지 마라

  • checkPass-word일 경우 암호 확인 작업만 하라
    • 세션 초기화하는 로직이 있을 경우 문제가 생길 수 있으므로 따로 initializeSession으로 함수를 분리하는 것이 좋다.

 

명령과 조회를 분리하라

  • 수행하거나 답하거나... 분리하라!

 

오류 코드 보다 예외 케이스를 사용하라

  • 명령 함수 에서 오류코드를 반환하는 방식은 명령, 조회 분리 규칙을 미묘하게 위반한다
if (deletePage(page) = E_OK) {
    if (registry.deleteReference(page.name) = E_OK) {
      if (configKeys.deleteKey(page.name.makeKey()) = E_OK){ 
          logger.log('page deleted');
      } else {
          logger.log('configKey not deleted');
      }
    } else {
    	logger.log('deleteReference from registry failed');
    }
} else {
	logger.log('delete failed'); 
    return E_ERR0R;
}

// 수정
try {
  deletePage(page);
  registry.deleteRefe rence(page.name); 
  configKeys.deleteKey(page.name.makeKey());
}catch (Exception e) { 
	logger.log(e.getMessage());
}

 

Try/Catch 블록 뽑아내기

  • 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 별도의 함수로 뽑아내는 것이 좋다.
public void delete(Page page) {
  try {
  	deletePageAndAllReferences (page);
  } catch (Exception e) { 
    logError(e);
    private void deletePageAndAHReferences(Page page) 

    throws Exception { 
      deletePage(page);
      registry.deleteReference(page.name); 
      configKeys.deleteKey(page.name.makeKey());
    }
    private void logError(Exception e) { 
      logger.log(e.getNlessage());
	}
}
  • 오류 처리도 한가지 작업이다.
  • Error.java 의존성 자석
    • enum 열거형 타입을 사용할 경우 의존성이 생겨서 문제가 될 수 있다. (import 해서 사용할 떄 변경이 생기면 매번 재 컴파일 해야하는 문제가 생길 수 있다.)
  • 반복하지 마라
    • 중복되는 코드를 없앨경우 가독성이 높아진다.
    • 객체 지향 프로그래밍은 코드를 부모 클래스로 몰아 중복을 없앤다.
  • 구조적 프로그래밍
    • 모든 블록에 입구와 출구는 하나만 존재해야한다
      • return 문이 하나여야한다. 하지만 함수가 작다면 사용해도 괜찮다.
      • goto문은 피해야한다.
  • 함수를 어떻게 짜죠?
    • 먼저 생각한대로 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐때까지 다듬고 정리한다.

 

 

내 생각....

역시 책은 내가 사용하는 언어로 된 것을 읽는 것이 가장 베스트인 것같다.

모르는 부분들 혹은 공감하지 못하는 부분들이 존재하는 것을 서서히 느끼고 있다.

하지만 큰 틀은 모두 동일하니 완독하는 것이 목표다.

다른 작업들도 같이 하고 있어 언제 완독할지는 모르겠다.

아 그리고 지금까지 읽은 부분으로 주변 사람들과 가끔 대화를 했는데, 

대부분은 맞는 말이지만 아니라고 생각하는 부분들이 존재하기때문에 융통성있게 이점을 배워가면 될 듯하다.

+ Recent posts