도서 요약 / / 2023. 1. 1. 19:53

좋은 코드, 나쁜 코드) 6장. 예측 가능한 코드를 작성하라

좋은 코드, 나쁜 코드 도서를 요약한 내용입니다.

6장. 예측 가능한 코드를 작성하라

6.1 매직값을 반환하지 말아야 한다

매지값의 일반적인 예는 값이 없거나 오류가 발생했음을 나타내기 위해 -1을 반환하는 것이다.

매지값은 함수의 정상적인 반환 유형에 들어맞기 때문에 이 갑이 갖는 특별한 의미를 인지하지 못하고, 이에 대해 적극적으로 경계하지 않으면 정상적인 반환값으로 오인하기 쉽다.

6.1.1 매직값은 버그를 유발할 수 있다

값이 없음을 나타내기 위해 함수에서 -1을 반환하는 것을 접한 적이 있을 것이다. 일부 레거시 코드와 언어의 내장된 함수들(예: 자바스크립트에서 배열에 대해 indexOf()를 호출할 때) 중에 이런 경우가 있다.

User 클래스 및 getAge() 함수

public class User {
  ...
    Integer getAge() { ...} // 사용자의 나이를 반환하는데 널값은 변환되지 않는다.
}

사용자 평균 연령 계산

double getMeanAge(List<User> users) {
  if (users.isEmpty()) {
    return null;
  }
  double sumOfAges = 0.0;
  for (User user : users) {
    sumOfAges += user.getAge().toDouble();
  }
  return sumOfAges / users.size().toDouble();
}

User 클래스의 상세 내용

public class User {
  private final Integer age;
  Integer getAge() {
    if (age == null) {
      return -1; // 나이가 주어지지 않으면 -1을 반환한다.
    }
    return age;
  }
}

6.1.2 해결책: 널, 옵셔널 또는 오류를 반환하라

6.4 입력 매개변수를 수정하는 것에 주의하라

6.4.1 입력 매개변수를 수정하면 버그를 초래할 수 있다

입력 매개변수 변경

private List<Invoice> getBillableInvoices(Map<User, Invoice> userInvoices, Set<User> usersWithFreeTrial) {
  userInvoices.removeAll(usersWithFreeTrial);
  return userInvoices.values();
}

private void processOrders(OrderBatch orderBatch) {
  Map<User, Invoice> userInvoices = orderBatch.getUserInvoices();
  Set<User> usersWithFreeTrial = orderBatch.getFreeTrialUsers();

  sendInvoices(getBillableInvoices(userInvoices, usersWithFreeTrial)); // getBillableInvoices는 예상과 다르게 userInvocies를 변경한다.
  endableOrderedServices(userInvoices); // 무료 평가판을 사용할 수 있는 유저는 해당 서비스를 사용할 수 있게끔 설정되지 않는다.
}

private void endableOrderedServices(Map<User, Invoice> userInvoices) {
  ...
}

6.4.2 해결책: 변경하기 전에 복사하라

입력 매개변수를 변경하지 않음

private List<Invoice> getBillableInvoices(Map<User, Invoice> userInvoices, Set<User> usersWithFreeTrial) {
  return userInvoices.
    .entries()
    .filter(entry -> !usersWithFreeTrial.contains(entry.getKey())) // filter()는 조건에 맞는 값을 새로운 리스트에 복사한다.
    .map(entry -> entry.getValue());
}

값을 복사하면 메모리나 CPU, 혹은 두 가지 모두와 관련해 성능에 영향을 미칠 수 있다. 하지만 입력 매개변수의 변경으로 인해 발생할 수 있는 예기치 못한 동작이나 버그와 비교하면 성능이 크게 문제되지 않는 경우가 많다.

6.6 미래를 대비한 열거형 처리

6.6.1 미래에 추가될 수 있는 열것값을 암묵적으로 처리하는 것은 문제가 될 수 있다

열것값의 암시적 처리

public enum PredictedOutcome {
  COMPANY_WILL_GO_BUST,
  COMPANY_WILL_MAKE_A_PROFIT
}

private boolean isOutcomeSafe(PredictedOutcome prediction) {
  if (prediction == PredictedOutcome.COMPANY_WILL_GO_BUST) {
    return false;
  }

  return true;
}

열거형의 새로운 값

public enum PredictedOutcome {
  COMPANY_WILL_GO_BUST,
  COMPANY_WILL_MAKE_A_PROFIT,
  WORLD_WILL_END
}

6.6.2 해결책: 모든 경우를 처리하는 스위치 문을 사용하라

public enum PredictedOutcome {
  COMPANY_WILL_GO_BUST,
  COMPANY_WILL_MAKE_A_PROFIT
}

private boolean isOutcomeSafe(PredictedOutcome prediction) {
  switch (prediction) {
    case COMPANY_WILL_GO_BUST:
      return false;
    case COMPANY_WILL_MAKE_A_PROFIT:
      return true;
  }

  throw new UncheckedException("Unhandled prediction: " + prediction);
}
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유