Home [Effective Java] Method
Post
Cancel

[Effective Java] Method

매개변수가 유효한지 검사하라

대부분의 메서드와 생성자는 매개변수가 특정 조건을 만족해야 원하는 동작을 할 수 있다. 매개변수에 대한 제약 조건은 메서드가 실행되기 전에 검증이 되어야 한다. 메서드가 동작하기 전에 오류를 잡아야 오류 발생 지점을 쉽게 찾을 수 있다. 만약 null이 아닌 0보다 큰 값을 요구하는 메서드가 있다고 가정한다면, 메서드가 동작하기 전에 값이 존재하고 양수인지 확인한 후 메서드를 실행 시키면 보다 더 안전하게 메서드를 사용할 수 있다. 또한 메서드 실행 전 오류를 감지할 수 있어 잘못된 값으로 메서드를 수행하기 전에 예외를 던질 수 있다. 메서드 내의 로직에서 발생하는 예외 상황과 메서드가 호출되기 전에 발생하는 예외 상황이 다를 수 있다. 이런 경우는 메서드 내에서 예외를 발생 시키고 상위 메서드로 던지는 것이 적합하다. API 설계자가 생성하는 메서드는 범용적으로 설계 되어야 한다. 이는 재사용성을 높일 수 있고 읽기 쉬운 코드를 생산할 수 있다.

적시에 방어적 복사본을 만들라

API를 설계할 때는 안전한 객체를 사용해야 한다. 클라이언트에 의해 변경 가능성이 큰 객체는 안전하지 않다. 객체를 만들 때는 의도하지 않은 변경을 막기 위해 노력해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
public class ObjectProtect {
  private String start;
  private String end;

  public ObjectProtect(ObjectProtect start, ObjectProtect end) {
    this.start = start.getStart();
    this.end = end.getEnd();

    if (this.start.compareTo(this.end) > 0) {
      throw new IllegalArgumentException();
    }
  }
}

생성자에서 매개변수 유효성을 검사하기 전에 방어적 복사본을 만들고 유효성을 검사해야 한다. 복사본을 사용하지 않으면, 외부에서 내부를 변경할 수 있는 가능성이 있다.

1
2
3
4
5
6
7
8
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class PrivateObject {
  private String name;
  private String phoneNumber;
}

위 코드는 객체의 값을 복사본으로 가져올 수 있고, setter를 막아 외부에서 직접 변경을 하지 못하도록 구성했다. API에서 값을 주고 받는 객체 모델을 만들 때 많이 사용하는 lombok annotation이다. 책에서 말하는 것과 같이 객체를 생성할 때는 생성자를 사용하지 않고, builder 패턴을 사용해 객체를 생성하도록 만들었다. 빈 생성자 접근 제한을 두어 외부에서 빈 생성자 호출을 막아 안전한 객체 생성 로직과 캡슐화 원칙을 지킬 수 있는 장점을 가지고 있다. 또한 객체의 값을 변경할 때는 setter를 따로 두기 보단 이전 포스팅에서 내용을 다룬 정적 팩토리 메서드를 이용해서 복사본을 반환하는 것도 좋은 방식이라고 생각한다.

Optional 반환은 신중히 하라

Java 1.8버전에서 Optional<T>이 등장한다. T타입을 참조하거나 아무것도 담지 않을 수 있다. Optional은 불변 컬렉션으로 원소를 최대 하나 가질 수 있다. 값이 없는 경우 null을 반환하거나 예외를 던지는 메서드보다 유연하게 사용할 수 있다. 또한 Java 1.8의 Stream API 대부분의 종단 연산은 Optional을 반환한다. Optional은 반환 값이 없을 수도 있음을 API 사용자에게 알려준다. 검사 예외와 유사하게 API 사용자는 반환 값이 없을 경우를 고려해서 코드를 작성해야 한다.

orElse()

빈 값인 경우 API 사용자는 기본값을 정할 수 있다. orElse(value) 형태로 반환값이 빈 경우 대체할 값을 정할 수 있다.

orElseThrow()

빈 값인 경우 원한는 예외를 던질 수 있다. orElseThrow(CustomException::new) 형태로 예외를 지정할 수 있다.

isPresent()

Optional에 값이 존재하면 true, 존재하지 않으면 false를 반환한다.

ifPresent()

Optional에 값이 있는 경우에 실행될 로직을 람다식이나 메서드 레퍼런스로 넘길 수 있다.

1
optionalMember.ifPresent(item -> item.doSomething());

get()

Optional에 값이 비어있지 않음이 보장되면 Optional 에서 T를 꺼낼 수 있다. get()을 사용하면 T를 반환한다.

Optional 잘 사용하기

Optional은 반환값 이외의 용도에 사용하기 적합하지 않다. 또한 Optional 반환에는 성능 저하가 따르기 때문에 성능이 중요한 부분에서는 예외를 던지는 것이 나을 수 있다. Optional에는 한가지 원소만 담아야 하며, 컬렉션, 배열, 스트림 등의 컨테이너 타입을 담지 않아야 한다. List로 예를 들면 Optional<List<» 보다 빈 List<>를 반환하는 것이 좋다.

This post is licensed under CC BY 4.0 by the author.

kafka consumer 예외 처리와 재시도 전략

대규모 트래픽을 고려한 장애 허용 with circuit breaker