반응형
생성자 대신 정적 팩터리 메서드를 고려하라
정적 팩토리 메서드(Static Factory Method)란 객체 생성의 역할을 하는 클래스 메서드라는 의미를 갖고 있다. 즉, 생성자를 통해 객체를 생성하는 것이 아닌 메서드를 통해 객체를 생성하는 것을 의미한다.
class Book {
private String title;
private long isbn;
public static Book createByIsbn(long isbn) {...};
public static Book createByTitle(String title){...};
}
- 클래스는 생성자와 별도로 정적 팩터리 메서드를 제공할 수 있다.
장점
- 이름을 가질 수 있다.
BigInter(int, int, Random)
보다BigInteger.probablePrime
이 가독성이 좋은 것처럼 생성자의 제한을 벗어나 정적 팩터리 메서드로 표현하는 것이 좋다.
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
- 인스턴스를 새로 생성하지 않아도 된다.
new
키워드를 사용하면, 객체는 반드시 새로 생성된다.valueOf
의 예시 처럼 이미 캐싱 된 숫자에 포함되면, 미리 생성된 객체를 반환할 수 있다. 이렇게 인스턴스의 수를 통제하는 것을 인스턴스 통제(instance-controlled)라고 한다.Flyweight pattern
(인스턴스 한 개만으로 여러 개의 '가상 인스턴스'를 제공하는 디자인 패턴)과 비슷한 기법- 불변 클래스의 인스턴스 미리 생성, 생성한 인스턴스 캐싱, 인스턴스 통제 클래스 (
Singleton
,Noninstantiable
,Enum
등)
public interface Book {
static Book createPoem() {
return new Peom();
}
static Book createMagazine() {
return new Magazine();
}
int getPrice();
}
class Peom implements Book {
@Override
public int getPrice() {
return 10000;
}
}
class Magazine implements Book{
@Override
public int getPrice() {
return 20000;
}
}
- 하위 타입 객체를 반환할 수 있다.
- 반환 타입을 인터페이스로 두어 유연한 API를 설계할 수 있다. 이는 인터페이스 기반 프레임워크를 만드는 핵심 기술이 된다.
- 자바 8 버전부터 인터페이스에 정적 메서드를 선언할 수 있다. 단,
public
이어야 한다.
- 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 반환타입의 하위 타입이기만 하면 다양항 클래스의 객체를 반환할 수 있다.
public static Connection getConnection() {
try {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
log.info("get connection={}, class={}", connection, connection.getClass());
return connection;
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
- 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
- 즉, 서비스 제공자 프레임워크를 만드는 근간이되며
JDBC
가 대표적인 예시다.Connection
이 서비스 인터페이스 역할,DriverManager.registerDriver
가 제공자 등록 API 역할,Driver.getConnection
이 서비스 접근 역할,Driver
가 서비스 제공자 인터페이스 역할을 한다.
- 즉, 서비스 제공자 프레임워크를 만드는 근간이되며
단점
- 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
- 인스턴스 통제 클래스를 구현하기 위해선
new
키워드를 생성하는 것을 막아야한다. 즉, 생성자를private
으로 만들게 되는데, 해당 클래스는 상속받을 수 없다. - 상속(Is-a 관계)보다 컴포지션(has-a 관계)을 사용하도록 유도하고, 불변 타입으로 만드려면 이 제약을 지켜야 한다는 점이 발생하는데, 오히려 장점이 될 수 있다.
- 인스턴스 통제 클래스를 구현하기 위해선
- 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
- 생성자처럼 API 설명에 명확히 드러나지 않으니, 사용자는 인스턴스화할 방법을 알아내야 한다. 즉, 네이밍과 API 문서화를 신경써야 한다.
정적 팩토리 메서드 예시
from
: 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드Date d = Date.from(instant)
of
: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
valueOf
: from과 of의 더 자세한 버전BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
instance
,getInstance
: 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스 임은 보장하지 않는다.StackWalker luke = StackWalker.getInstance(options);
create
,newInstance
: 매번 새로운 인스턴스를 생성해 반환함을 보장Object newArray = Array.newInstance(classObject, arrayLen);
getType
: getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용FileStore fs = Files.getFileStore(path);
newType
: newInstance와 같으나, 다른 클래스에 팩터리 메서드를 정의할 때 사용BufferedReader br = Files.newBufferedReader(path);
type
: getType과 newType의 간결한 버전List<Complaint> litany = Collections.list(legacyLitany);
REFERENCES
https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/
반응형