[정적 팩토리 메소드란?]
정적 팩토리 메소드란 객체를 생성하는 메소드를 말한다.
그렇다면 생성자를 사용하지 않고, 정적 팩토리 메소드를 사용하는 이유는 무엇일까?
생성자의 경우 다음과 같이 오버로딩을 하면, 새로운 생성자들을 만들 수 있지만, 생성자에 어떤 매개변수가 필요한지 알 수 없다.
이와 달리 정적 팩토리 메소드는 생성자가 가지지 못한 여러 장점들을 가지는데 하나씩 알아보자.
[1. 이름을 가진 생성자]
public class Member {
MemberType memberType;
String name;
public Member(MemberType memberType, String name) {
this.memberType = memberType;
this.name = name;
}
public Member(String name) {
this.memberType = MemberType.NORMAL;
this.name = name;
}
}
public class main {
public static void main(String[] args) {
Member lisa = new Member(ADMIN, "lisa");
// 해당 생성자로는 어떤 역할을 하는 회원을 생성하는지 알 수 없다.
Member member = new Member("suchan");
}
}
위와 같은 클래스에서는 new Member("suchan")가 어떤 역할을 하는 회원을 생성하였는지 알 수 없다.
정적 팩토리 메소드는 이름을 가지기에 어떤 역할을 하는 회원을 만들었는지 알 수 있다.
public class Member {
MemberType memberType;
String name;
private Member(MemberType memberType, String name) {
this.memberType = memberType;
this.name = name;
}
private Member(String name) {
this.memberType = MemberType.NORMAL;
this.name = name;
}
public static Member createNormalMember(String name) {
return new Member(name);
}
}
public class main {
public static void main(String[] args) {
Member member = createNormalMember("suchan");
}
}
정적 팩토르 메소드의 이름(createNormalMember)을 통해 memberType이 Normal인 회원을 생성한다는 것을 알 수 있다.
결국 정적 팩토르 메소드는 어떤 회원이 반환되는지를 유추할 수 있어,
코드의 가독성을 높여준다.
[2. 호출할 때 마다 새로운 인스턴스를 생성하지 않아도 된다.]
enum 타입과 같이 자주 사용하는 요소가 정해져있거나, 사용되는 값의 개수가 정해져 있으면 정적 팩토리 메소드를 통해 해당 값을 미리 생성해두고, 조회할 수 있는 구조로 만들 수 있다.
주로 같은 객체가 자주 요청되고, 객체의 생성비용이 크다면, 정적 팩토리 메소드를 활용하여 객체를 생성하자.
public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
...
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
[3. 반환 타입의 하위 타입 객체 반환 가능]
정적 팩토리 메소드를 사용하여, 상속을 활용하면 반환 타입의 하위 타입을 반환할 수 있다.
다음과 같이 과일의 당도에 따라 과일이 지정되어있는 경우, 분기처리를 통해
하위 타입 객체를 반환할 수 있다.
public class Fruit {
int brix; // 당도
public static Fruit from(int brix) {
if (brix > 12) {
return new Banana();
} else if (brix > 10) {
return new Apple();
} else {
return new StrawBerry();
}
}
}
[4. 입력 변수에 따라 매번 다른 클래스 객체를 반환 가능]
EnumSet의 경우 관리하는 원소의 개수가 64개 이하인 경우 ReqularEnumSet을 반환하고, 이상인 경우 JumboEnumSet을 반환한다.
이와 같은 경우 개발자는 EnumSet를 상속받은 ReqularEnumSet과 JumboEnumSet의 존재를 모르더라도, EnumSet사용할 수 있게 해준다.
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
[5. 객체의 생성을 캡슐화]
정적 팩토리 메소드를 이용해 객체의 생성을 캡슐화할 수 있는데, 대표적인 예로는 dto를 entity로 entity를 dto로 변환할때 사용가능하다.
@Getter
public class MemberDto {
private String name;
private MemberType memberType;
public static Member from(Member member) {
return new Member(member.getName(), member.getMemberType());
}
}
기본적으로 db에 저장하는 경우를 제외하고는 entity 대신 dto를 사용하여 entity 스팩을 노출하지 않는데, 정적 팩토리 메소드를 사용하면, 내부 구현을 외부에 보여주지 않고, 쉽게 변환할 수 있다.
물론 정적 팩토리 메소드 또한 단점이 존재한다.
[1. private 생성자의 경우 상속 불가능]
=> 부모클래스를 자식클래스에 상속하기 위해서는 생성자의 접근제어자가
public이거나 protected여야 하는데, 정적 팩토리 메소드만 제공할 경우
상속이 불가능하다.