Dev/Books

[CleanCode] 3장. 함수

kyeoneee 2020. 5. 6. 01:28
반응형

목차

  1. 작게 만들어라!
  2. 한 가지만 해라!
  3. 함수 당 추상화 수준은 하나로!
  4. Switch 문
  5. 서술적인 이름을 사용하라
  6. 함수 인수
  7. 부수 효과를 일으키지 마라!
  8. 명령과 조회를 분리하라!
  9. 오류 코드보다 예외를 사용하라!
  10. 반복하지 마라!
  11. 구조적 프로그래밍
  12. 함수를 어떻게 짜죠?
  13. 결론

작게 만들어라!

작게의 기준은 무엇일까?

  • 블록과 들여쓰기
    if/else/switch 문 등에 들어가는 블록은 한 줄이며 indent는 2단을 넘어서면 안된다.
    적절한 메서드명을 가지는 메서드를 호출하는 방식을 통해 이를 충족시킨다.

한 가지만 해라!

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

함수는 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
한 함수에서 섹션을 나눌 수 없다면 함수가 한 가지 작업을 한다고 할 수 있다.

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

함수 내에 추상화 수준이 섞이면 특정 표현이 근본 개념인지 세부사항인지 구분하기 어려워진다.

  • 위에서 아래로 코드 읽기: 내려가기 규칙
    한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 오면 함수 추상화 수준이 한번에 한 단계씩 낮아진다. 코드는 위에서 아래로 이야기처럼 읽혀야 좋다.

Switch 문

Bad :

public Money calculatePay(Employee e) throws InvalidEmployeeType {
  switch (e.type) {
    case COMMISSIONED:
      return calculateCommissionedPay(e);
    case HOURLY:
      return calculateHourlyPay(e);
    case SALARIED:
      return calculateSalariedPay(e);
    default:
      throw new InvalidEmployeeType(e.type);
  }
}

Switch문의 문제점

  1. 함수가 길어진다
  2. '한 가지' 작업만 수행하지 않는다.
  3. Single Response Principle를 위반한다.
  4. Open Closed Principle을 위반한다.

Switch문은 다형적 객체를 생성하는 코드안에서만 사용하도록 해야 한다.

서술적인 이름을 사용하라

일관적이고 서술적인 이름을 사용하여 함수 이름만으로 해당 함수가 하는 일을 파악할 수 있도록 해야 한다.

함수 인수

테스트 코드 관점에서도 인자가 많을수록 테스트를 위한 유효한 인자를 만들어야 하는 문제가 생긴다.
가장 이상적인 인수 개수는 0개, 최대 3개. 하지만 적을수록 좋다. 줄이자 줄이자!

  • 많이 쓰는 단항 형식
    • 인수에 질문을 던지는 경우
    • 인수를 뭔가로 변환해 결과를 반환하는 경우
    • 이벤트 (출력은 없는 경우, 다소 드물다) - 이름과 문맥을 통해 이벤트 함수임을 명확히 드러내야 한다.
  • 플래그 인수
    플래그 인수 값에 따라 메서드가 다른 기능을 하게 된다는 의미이므로 플래그 인수는 지양해야 한다.
  • 이항 함수
    무시해도 되는 인자가 있는 순간 버그 확률이 늘어난다.
    이항 함수가 적절한 경우는 인수 2개가 한 값을 표현하는 두 요소인 경우, 자연적인 순서가 있는 경우이다.
    같은 클래스인 2개의 인자를 받는 경우 순서를 헷갈릴 수 있으므로 지양해야 한다.
  • 삼항 함수
    순서, 주춤, 무시로 야기되는 문제는 두 배 이상으로 늘어난다.
    인자의 의미가 명확한 경우가 아니면 지양한다.
  • 인수 객체
    결국 변수에 이름을 붙여야 하므로 개념을 표현하게 되는 것이다.
    e.g.,
    Circle makeCircle(double x, double y, double radius);
    Circle makeCircle(Point center, double radius);
  • 인수 목록
    가변 인수 전부가 동등하게 취급될 경우 List 형 인수 하나로 취급할 수 있다.
    e.g., public String format(String format, Object... args)
  • 동사와 키워드
    함수의 의도나 인수의 순서와 의도를 함수 이름에 담아야 한다.
    함수 이름에 인자의 키워드를 추가하여 인자의 의미와 순서를 보장 해 준다.

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

함수는 꼭 한 가지 기능만을 한다.
부수 효과로 발생 가능한 문제?

  • Temporal Coupling
  • Order Dependency

명령과 조회를 분리하라!

함수의 역할

  • 뭔가를 수행한다.
  • 뭔가에 답한다.

Bad :

// attribute인 속성을 찾아 값을 value로 설정 후 성공시 true, 실패시 false
public boolean set(String attribute, String value);

if (set("username", "unclebob")) ...

Good :

if (attributeExist("username")) {
  setAttribute("username", "unclebob");
}

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

오류코드의 문제점

  • 호출자는 오류 코드를 바로 처리해야 한다.
  • 의존성 자석 : 오류 코드의 수정/추가/삭제가 일어날 경우 처리 코드도 수정해야 한다

Try/Catch 블록 뽑아내기
오류처리도 한 가지 작업임을 인지하고 오류 처리 함수를 분리하도록 한다.

Bad : try/catch 블록은 정상 동작과 오류 처리 동작을 섞으며 코드 구조에 혼란을 일으킨다.

try {
  deletePage(page);
  registry.deleteReference(page.name);
  configKeys.deleteKey(page.name.makeKey());
} catch (Exception e) {
  logger.log(e.getMessage());
}

Good :

public void delete(Page page) {
  try {
    deletePageAndAllReferences(page);
  } catch (Exception e) {
    logError(e);
  }
}

// 정상 동작만을 처리한다.
private void deletePageAndAllReferences(Page page) throws Exception {
  deletePage(page);
  registry.deleteReference(page.name);
  configKeys.deleteKey(page.name.makeKey());
}

private void logError(Exception e) {
  logger.log(e.getMessage());
}

반복하지 마라!

반복되는 코드가 다른 코드와 섞이면 모양이 달라지면서 중복이 잘 드러나지 않게 되어 수정이 일어날 때 오류가 발생할 확률이 높아진다.

중복을 없애거나 제어 할 목적의 원칙과 기법들

  • 관계형 데이터베이스에 정규 형식을 만듦
  • 객체지향 프로그래밍 - 코드를 부모 클래스로 몰아 중복을 없앰
  • 구조적 프로그래밍
  • AOP(Aspect Oriented Programming) - Spring의 핵심 개념 중 하나로 부가기능의 모듈화 (하단 링크들 참고)
  • COP(Component Oriented Programming) - 기존 시스템이나 소프트웨어를 구성하는 컴포넌트를 조립해서 하나의 새로운 응용프로그램을 만드는 소프트웨어 개발 방법론

구조적 프로그래밍

모든 함수와 함수 내 모든 블록에 entry와 exit이 하나만 존재해야 한다. 즉, return 문이 하나여야 한다. 루프 안에서 break, continue를 사용해서는 안 되며 goto는 절대로 안된다.

함수가 클 때만 이익을 제공하는 기법이다.
함수가 작을때는 return, break, continue를 여러차례 사용해도 괜찮지만 goto는 작은 함수에서도 피해야 한다.

함수를 어떻게 짜죠?

TDD
테스트 코드 -> 중복, 네이밍, 들여쓰기 등 상관없이 우선 기능을 개발 -> 리팩토링 (무조건 단위테스트는 계속 통과)
위 과정을 반복한다.

결론

위의 규칙들을 잘 따라 시스템을 잘 풀어가야한다!


참고하면 좋을 문서

AOP

반응형

'Dev > Books' 카테고리의 다른 글

[CleanCode] 7장. 예외 처리  (0) 2020.05.16
[CleanCode] 6장. 객체와 자료 구조  (0) 2020.05.15
[CleanCode] 5장. 형식 맞추기  (0) 2020.05.12
[CleanCode] 4장. 주석  (0) 2020.05.12
[CleanCode] 2장. 의미있는 이름  (0) 2020.05.02