생성자와 비교하여 정적 팩토리 메서드가 가지는 장점은 아래와 같다.
하나씩 살펴보도록하자.
여러개의 생성자를 가지는 경우에도 생성자에는 이름을 부여할 수 없다. 단지 입력 매개변수에 따라 그에 해당하는 생성자가 맵핑될 뿐이다. 반면 정적 팩토리 메소드는 이름을 가질 수 있기에 보다 명확하고 반환될 객체의 특성을 쉽게 묘사 할 수 있다.
예를 들어 값이 소수인 BigInteger를 반환하는 객체를 만든다고 가정할 때 BigInteger(int, int, Random) 과 BingInteger.problePrime 중 어느 쪽이 의미를 잘 전달하는지 생각해보면 이름을 가진다는 것에 대한 이점을 이해 할 수 있을 것이다.
인스턴스를 반드시 새롭게 만들 필요가 없이 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다.. 때문에 생성비용이 큰 객체가 자주 요청되는 상황에서 큰 성능향상을 노려볼 수 있으며, 인스턴스 통제가 가능하다.
생성자는 리턴값이 없는 반면, 정적 팩토리 메소드는 반환값을 가진다. 이 때 반환 객체를 하위 클래스로 지정 할 수 있다. 반환할 객체의 클래스를 자유롭게 선택할 수 있어 엄청난 유연성을 가진다.
class OrderUtil {
public static Discount createDiscountItem(String discountCode) throws Exception {
if(!isValidCode(discountCode)) {
throw new Exception("잘못된 할인 코드");
}
// 쿠폰 코드인가? 포인트 코드인가?
if(isUsableCoupon(discountCode)) {
return new Coupon(1000);
} else if(isUsablePoint(discountCode)) {
return new Point(500);
}
throw new Exception("이미 사용한 코드");
}
}
class Coupon extends Discount { }
class Point extends Discount { }
위 두가지는 아직까지 내 수준에서 이해가 제대로 가지 않았다. 추후에 이해가 되는 시점에 추가적인 내용을 보충 하겠다
이제 단점에 대해 살펴보자
상속을 위해서는 public이나 protected 생성자가 필요하다. 때문에 정적 팩터리 메소드만으로는 하위 클래스를 만들 수 없다. 그어찌보면 이 제약은 상속보다 컴포지션을 사용하도록 유도하고, 불변타입으로 만들려면 이 제약을 지켜야한다는 점에서 오히려 장점으로 받아 들일 수 도 있다.
생성자처럼 API 설명에 명확하게 드러나지 않아, 사용자가 정적 메스드를 찾아내야한다. 이를 극복하기 위해 메서드 이름으로 널리 알려진 규약을 따라 짓는다. 규약은 아래와 같다.
from : 매개변수 하나를 받아서 해당 타입의 인스턴스 반환하는 형변환 메서드
Date d = Date.from(instant)
of : 여러 매개변수를 받아, 적합한 타입의 인스턴스를 반환하는 집계 메서드
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
instance 혹은 getInstance : 매개변수로 명시한 인스턴스 반환, 같은 인스턴스임을 보장하지는 않는다.
StackWalker luke = StackWalker.getInstance(options);
create 혹은 newInstance : 매번 새로운 인스턴스를 생성해 반환
Object newArray = Array.newInstance(classObject, arrayLen);
getType : getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용
FileStore fs = Files.getFileStore(path);
서칭하다 우연히 들어와졌는데 좋네요 ㅋㅋㅋ