public 생성자 보단 정적 메서드로 제공하는 것이 유리하다.
장점 1. 이름을 가질 수 있다.
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
여기서 빈 SecurityContext 객체를 생성한다는 것을 직관적으로 알 수 있다.
반환될 객체의 특성을 쉽게 묘사 가능
장점 2. 호출 될 때 마다 새로운 객체생성을 하지 않아도 된다.
class ColorCollect {
private static final Map<String, Color> colorMap = new HashMap<>();
public static Color getColor(String colorName) {
Color color = colorMap.get(colorName);
if (color == null) {
Color newColor = new Color(colorName);
colorMap.put(colorName, newColor);
return newColor;
}
return color;
}
}
Color red = ColorCollect.getColor("red");
객체를 생성하지않고, 캐싱된 객체를 재활용하게 할 수 있다.
불필요한 객체생성을 막고, 인스턴스를 통제할 수 있다.
* Flyweight 패턴 / Singleton 패턴
장점 3. 반환 타입의 하위 타입 객체를 반환할 수 있다.
static <T> Set<T> synchronizedSet(Set<T> s, Object mutex) {
return new SynchronizedSet<>(s, mutex);
}
ex) java.util.Colletions의 정적 팩터리 메소드
반환할 객체를 자유롭게 선택할 수 있는 유연성이 있다.
여기서 사용자는 반환 객체인 구현체 SynchronizedSet의 상세내용을 알지 못해도,
Set Collection의 사용방법, 즉 인터페이스만 알아도 무관하다.
장점 4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
interface Membership {
static Membership createMembership(String grade) {
if ("A".equals(grade)) {
return new VipMembership();
}
if ("B".equals(grade)) {
return new GeneralMembership();
}
throw new IllegalArgumentException("No Grade");
}
int discount(int price);
}
class VipMembership implements Membership {
@Override
public int discount(int price) {
return price - 10;
}
}
class GeneralMembership implements Membership {
@Override
public int discount(int price) {
return price - 5;
}
}
public class Main {
public static void main(String[] args) {
Membership membership = Membership.createMembership("A");
System.out.println(membership.discount(100));
}
}
장점 3번의 연장이라고 생각됨.
파라미터에 따라 필요한 구현체를 반환 할 수 있다.
마찬가지로 하위 객체를 반환할 수 있는 유연성으로 인하여 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있고
클라이언트는 구현 객체에 구애받지 않고 명시된 인터페이스로 이용 가능
장점 5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
// DriverManager.register(new Driver());
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD);
이런 유연함의 예로 서비스 제공자 프레임워크인 JDBC를 예시로 들고 있다.
1. 구현체의 동작을 정리하는 서비스 인터페이스 -> Connection
2. 제공자가 구현체를 등록할 떄 사용하는 제공자 등록 API
-> 소스에선 Class.forName("com.mysql.jdbc.Driver") = DriverManager.registerDriver();
3. 클라이언트가 서비스의 인스턴스를 얻을 때 사용하는 서비스 접근 API -> DriverManager.getConnection()
4. 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체를 설명 -> Driver 인터페이스
여기서 정적 팩터리 메서드인 DriverManager.getConnection()는
작성 당시에 반환 객체의 클래스(Connection 구현체)를 알 필요가 없다.
단점 1. 정적 팩터리 메서드만 제공 시 상속 불가능
class AClass {
private AClass() {}
public static AClass createAClass() {
return new AClass();
}
}
class BClass extends AClass { // 컴파일 에러
}
단점 2. 생성자 보다 메서드를 찾기 힘들다
생성자 처럼 명확히 드러나지 않으니 인스턴스화 할 방법을 따로 찾아야한다.
흔히 사용되는 명명 방식이 존재
명명 방식
from : 매개변수를 하나 받아서 해당 타입의 인스턴스로 반환하는 형변환 메서드
of : 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환
valueOf : from과 of의 더 자세한 버전
instance / getInstance : 매개변수로 명시한 인스턴스 반환, 같은 인스턴스임을 보장하지는 않음
create / newInstance : 새로운 인스턴스를 생성해 반환함을 보장
getType : 생성 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용
newType : 생성 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용 (새로운 인스턴스 보장)
type : getType/newType의 간결한 버전
'프로그래밍 > JAVA' 카테고리의 다른 글
[JAVA] Effective Java - 3. private 생성자나 열거 타입으로 싱글턴 보증 (0) | 2022.08.31 |
---|---|
[JAVA] Effective Java - 2. 점층적 생성자, 자바 빈즈, 빌더 패턴 (0) | 2022.08.17 |
[JAVA] JVM 구조와 동작 2 (Runtime Data Area, Garbage Collector) (0) | 2022.04.04 |
[JAVA] JVM 구조와 동작 1 (JVM, Class Loader, Execution Engine) (0) | 2022.03.31 |
[JAVA] Lombok Builder 사용 시 Generic 처리 (0) | 2021.12.06 |