[JAVA] 정적 팩토리 메서드

hyelim·2023년 8월 16일
0
post-thumbnail

“생성자 대신 정적 팩토리 메서드를 고려하라”

정적 팩토리 메서드란

정적(static)팩토리(factory)메서드(method)

GOF 의 디자인패턴 중 팩토리 패턴에서 유래한 단어로 객체를 생성하는 역할을 분리하겠다는 취지가 담겨있다

즉, 정적 팩토리 메서드란 객체 생성의 역할을 하는 클래스 메서드라는 의미로 생각하면된다

예를 들어 아래 코드에서 직접적으로 생성자를 통해 LocalDateTime 객체를 생성하는 것이 아니라 메서드를 통해 객체를 생성하는 것을 정적 팩토리 메서드라고 한다

// LocalTime.class
...
public static LocalTime of(int hour, int minute) {
  ChronoField.HOUR_OF_DAY.checkValidValue((long)hour);
  if (minute == 0) {
    return HOURS[hour];
  } else {
    ChronoField.MINUTE_OF_HOUR.checkValidValue((long)minute);
    return new LocalTime(hour, minute, 0, 0);
  }
}
...

// hour, minutes을 인자로 받아서 9시 30분을 의미하는 LocalTime 객체를 반환한다.
LocalTime openTime = LocalTime.of(9, 30);

외부에서 원하는 객체를 반환하는 형태를 띤다면, 정적 팩토리 메서드라고 이해해보겠다

정적 팩토리 메서드의 장점

  1. 이름을 가질 수 있다

    정적 팩토리 메서드를 사용하면 해당 생성의 목적을 이름에 표현할 수 있어 가독성이 좋아지는 효과가 있다.

    public class LottoFactory() {
      private static final int LOTTO_SIZE = 6;
    
      private static List<LottoNumber> allLottoNumbers = ...; // 1~45까지의 로또 넘버
    
      public static Lotto createAutoLotto() {
        Collections.shuffle(allLottoNumbers);
        return new Lotto(allLottoNumbers.stream()
                .limit(LOTTO_SIZE)
                .collect(Collectors.toList()));
      }
    
      public static Lotto createManualLotto(List<LottoNumber> lottoNumbers) {
        return new Lotto(lottoNumbers);
      }
      ...
    }

    위의 코드에서 볼 수 있듯이 목적에 따라 정적 팩토리 메서드 명을 다르게 객체를 생성할때 특정 이름을 지정할 수 있다

  2. 호출할 때마다 새로운 객체를 생성할 필요가 없다.

    자주 사용되는 요소의 개수가 정해져있다면 해당 개수만큼 미리 생성해놓고 조회(캐싱)할 수 있는 구조로 만들수 있다. 정적 팩터리 메서드와 캐싱구조를 함께 사용하면 매번 새로운 객체를 생성할 필요가 없어진다.

    public class LottoNumber {
      private static final int MIN_LOTTO_NUMBER = 1;
      private static final int MAX_LOTTO_NUMBER = 45;
    
      private static Map<Integer, LottoNumber> lottoNumberCache = new HashMap<>();
    
      static {
        IntStream.range(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER)
                    .forEach(i -> lottoNumberCache.put(i, new LottoNumber(i)));
      }
    
      private int number;
    
      private LottoNumber(int number) {
        this.number = number;
      }
    
      public LottoNumber of(int number) {  // LottoNumber를 반환하는 정적 팩토리 메서드
        return lottoNumberCache.get(number);
      }
    
      ...
    }

    위의 코드에서 주목해야할 점은 정적 팩토리 메서드로만 객체 생성이 가능하도록 number 를 private으로 설정한 점이다

  1. 하위 자료형 객체를 반환할 수 있다.
    하위 자료형 객체를 반환하는 정적 팩토리 메서드의 특징은 상속을 사용할 때 확인할 수 있다. 이는 생성자의 역할을 하는 정적 팩토리 메서드가 반환값을 가지고 있기 때문에 가능한 특징이다.

  2. 객체 생성을 캡슐화 할 수 있다

    정적 팩토리 메서드를 사용하면 내부 구현을 모르더라도 쉽게 변환할 수 있다.

    public class CarDto {
      private String name;
      private int position;
    
      pulbic static CarDto from(Car car) {
        return new CarDto(car.getName(), car.getPosition());
      }
    }
    
    // Car -> CatDto 로 변환
    CarDto carDto = CarDto.from(car);

    아래는 정적팩토리메서드를 사용하지 않은 경우이다. 코드를 보면 알 수 있듯이 외부에서 생성자 내부 구현을 모두 해체한 것을 확인할 수 있다

    CarDto carDto = new CarDto(car.getName(), car.getPosition); // 생성자를 쓴 경우

정적팩토리메서드 네이밍 컨벤션

  • from : 하나의 매개 변수를 받아서 객체를 생성
  • of : 여러개의 매개 변수를 받아서 객체를 생성
  • getInstance | instance : 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
  • newInstance | create : 새로운 인스턴스를 생성
  • get[OtherType] : 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
  • new[OtherType] : 다른 타입의 새로운 인스턴스를 생성.

정적팩토리메서드를 지향해야하는이유

정적 팩토리 메서드는 단순히 생성자의 역할을 대신하는 것 뿐만 아니라, 우리가 좀 더 가독성 좋은 코드를 작성하고 객체지향적으로 프로그래밍할 수 있도록 도와 준다

profile
기록용

0개의 댓글