250x250
반응형
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

재 현

정적 팩토리 메서드 본문

Development

정적 팩토리 메서드

본명은이점례 2023. 11. 2. 19:35
728x90

정적 팩토리 메서드란, 객체 생성의 역할을 하는 클래스 메서드다.

 

// 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);

 

우리가 흔히 아는 생성자를 통한 생성이 아닌, of라는 메서드를 사용해서 객체를 생성하는 것을 의미한다. 

 

public enum ErrorMessage {
	OVER_NAME_LENGTH("이름 길이가 초과되었습니다."),
    ;
}

{
	ErrorMessage.valueOf(OVER_NAME_LENGTH);
}

 

그리고 우테코 진행하다보면, 많은 사람들이 enum을 사용해서 쓰는 걸 볼 수 있다. enum class에서 나중에 가져와서 쓸 때도 역시 정적 팩토리 메서드에 해당한다. 

 

그렇다면 무슨 이점이 있을까?

 

이펙티브 자바에선 “생성자 대신 정적 팩토리 메서드를 고려하라”라는 아이템을 소개한다.

 

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);
  }
  ...
}

 

Lotto = new Lotto() 이렇게 생성하는 것보다, 어떤 목적으로 객체를 생성하는지 명확하게 알 수 있었다. Auto로또와 ManualLotto 이렇게 구분지음으로써 생성 목적을 파악할 수 있다.

 

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

enum과 같이 자주 사용되는 요소의 개수가 정해져있다면 해당 개수만큼 미리 생성해놓고 조회(캐싱)할 수 있는 구조로 만들수 있다. 

 

 

여기서 캐싱이란?

 

캐시는 동일한 데이터에 반복해서 접근해야 하거나 많은 연산이 필요한 일일때, 결과를 빠르게 이용하고자 성능이 좋은 혹은 가까운 곳에 저장하는 것이다. 말 그대로 컴퓨터 과학에서 데이터나 값을 미리 복사해 놓는 임시 장소를 가리킨다. 그렇다면 캐싱이란 미리 생성해놓은 것을 조회하는 것을 의미한다.

 

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);
  }

  ...
}

 

static 메서드로 lotterNumberCache에 LottoNumber를 미리 저장해놓고, 매번 객체를 새로 생성하는 것을 막았다. 또한 생성자를 private하게 막음으로써 따로 정해진 범위 (1~45)를 벗어난 객체를 생성할 가능성을 없앴다.

 

3. 하위 자료형 객체를 반환할 수 있다.

public class Level {
  ...
  public static Level of(int score) {
    if (score < 50) {
      return new Basic();
    } else if (score < 80) {
      return new Intermediate();
    } else {
      return new Advanced();
    }
  }
  ...
}

 

우리는 Level를 생성할 때, 점수를 인자로 넣어주면은 각각 조건에 따라 하위 객체로 return 할 수 있다. 이는 코드를 깔끔하게 유지시켜주는 좋은 기능이다.

 

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

 

여기서 캡슐화란?

데이터와 해당 데이터를 다루는 메서드(또는 함수)를 하나의 단위로 묶는 것을 의미한다. 캡슐화는 데이터의 은닉성(Encapsulation)과 데이터와 메서드를 캡슐로 묶음으로써 정보 은닉(Information Hiding)이라고도 불린다.

 

우리는 웹 어플리케이션을 개발할 때, 계층 간에 데이터를 전송하기 위한 객체로 DTO(Data transfer object)를 정의한다.

 

여기서 DTO란?

필요한 정보만을 노출시킴으로써 데이터의 무결성을 유지하고, 외부 시스템에서 데이터를 직접 조작하지 못하도록 보호한다.

 

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);

 

만약에 정적 팩토리 메서드(from)을 쓰지 않고 생성한다면,

 

Car carDto = CarDto.from(car); // 정적 팩토리 메서드를 쓴 경우
CarDto carDto = new CarDto(car.getName(), car.getPosition); // 생성자를 쓴 경우

 

이런 식으로 외부에서 생성자 내부의 로직을 다 드러내야 할 것이다. 

 

 

그다음으로 알아야 할 건 다음은 네이밍 컨벤션이다.

 

 

오라클 문서에서 타입들을 명시해놨다. 그 중에 자주 쓰이는 건 

 

  • of : 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메소드
  • from: 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메소드
  • getInstance | instance : 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
  • newInstance | create : 새로운 인스턴스를 생성
  • get[OtherType] : 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
  • new[OtherType] : 다른 타입의 새로운 인스턴스를 생성.

 

 

참고


https://velog.io/@effypark/%EC%BA%90%EC%8B%9C-%EC%BA%90%EC%8B%B1%EC%9D%B4%EB%9E%80 

https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/

https://docs.oracle.com/javase/tutorial/datetime/overview/naming.html

728x90