@Test
public void isZeroWhenStarted() {
Location location = new Location();
...
}
이펙티브 자바 아이템 1를 참고해보면 좋을 거 같아요 :)
여기서 말하는 디자인 패턴의 팩토리 메서드와 다른 것이다. public
을 이용한 생성자 대신 static
팩토리 메서드를 제공하는 것이다.
정적 팩토리 메서드란 객체 생성의 역할을 하는 클래스 메서드다.
java.time.LocalTime
// 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);
실제 java.time 패키지에 포함된 LocalTime 클래스의 코드다.
메서드명이 of
이며 생성자를 통해서 객체를 생성하는 것이 아닌 메서드를 통해서 객체를 생성하고 있다.
그렇다면 어떤 장점이 있길래 있는 생성자를 놔두고 이런걸 쓰는가?
이펙티브 자바에서는 처음으로 소개하는 아이템이 바로 이 정적 팩토리 메서드라고 한다.
public static Position createStartPosition() {
return new Position(START_POSITION_VALUE);
}
나는 코드 리뷰를 받고 메서드를 이렇게 변경해보았다.
Position position = Position.createStartPosition();
이렇게 메서드 명을 통해 더욱 명시적으로 무엇이 생성되었는지 드러난다.
만약 자주 사용되는 것이라면 static으로 만들고 캐싱하게 할 수도 있다.
또한 생성자를 private으로 설정해서 객체 생성 방법을 제한할 수 있다.
하위 자료형 객체를 반환하는 정적 팩토리 메서드의 특징은 상속을 사용할 때 확인할 수 있다. 이는 생성자의 역할을 하는 정적 팩토리 메서드가 반환값을 가지고 있기 때문에 가능한 특징이다.
Basic, Intermediate, Advanced 클래스가 Level라는 상위 타입을 상속받고 있는 구조를 생각해보자. 시험 점수에 따라 결정되는하위 등급 타입을 반환하는 정적 팩토리 메서드를 만들면, 다음과 같이 분기처리를 통해 하위 타입의 객체를 반환할 수 있다.
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();
}
}
...
}
웹 어플리케이션을 개발하다보면 계층 간에 데이터를 전송하기 위한 객체로 DTO(Data transfer object)를 정의해서 사용한다.
DTO와 Entity간에는 자유롭게 형 변환이 가능해야 하는데, 정적 팩터리 메서드를 사용하면 내부 구현을 모르더라도 쉽게 변환할 수 있다.
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);
만약 정적 팩토리 메서드를 쓰지 않고 DTO로 변환한다면 외부에서 생성자의 내부 구현을 모두 드러낸 채 해야할 것이다.
Car carDto = CarDto.from(car); // 정적 팩토리 메서드를 쓴 경우
CarDto carDto = new CarDto(car.getName(), car.getPosition); // 생성자를 쓴 경우