본문 바로가기
Language/JAVA

[Effective java] 정적 팩터리 메서드

by jepa 2023. 7. 29.
728x90
SMALL

목적

객체 생성을 생각하면 나는 보통 public 생성자를 떠올린다.

클라이언트에서 객체 생성을 할 때, public 생성자를 이용한 방법이 아닌

정적 팩터리 메서드를 이용한 방법을 알아보자.

  • 정적 팩터리 메서드에 대해서 알아보자
  • 정적 팩터리 메서드의 장단점에 대해 알아보자
  • 책을 읽고 정리하자

정적 팩터리 메서드

클래스의 인스턴스를 반환하는 단순한 정적 메서드

예시
boolean의 값을 받아 Boolean 객체 참조로 변환하는 코드

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}​

장점

  • 1. 이름을 가질 수 있다.
    • 반환될 객체의 특성을 쉽게 묘사 가능
      • 💡 임의의 소수 값 BigInteger 반환
        BigInteger(int,int,Random)보다 BigInteger.probablePrime이 의미가 더 명확
    • 생성자는 역할을 기억하기 어려움
  • 2.호출마다 인스턴스 생성이 필요없다.
    • 불필요한 객체 생성을 피할 수 있다.
    • 같은 객체가 자주 요청되는 상황에서 유용하다.
    • 인스턴스 통제(instance-controlled) 클래스로 만들 수 있다.
      • 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아 있게 할지를 통제 가능하다.
      • 💡 인스턴스를 통제로 가능한 일
        1. 싱글턴
        2. 인스턴스화 방지
        3. 동치인 인스턴스가 단 하나뿐임을 보장(a==ba.equals(b))
  • 3. 반환 타입의 하위 타입 객체를 반환할 수 있다.
    • 반환할 객체의 클래스 선택이 가능하다 → 유연성 증가
    • 구현 클래스 공개가 필수가 아니다. → API 작게 유지 가능

💡 인터페이스 기반 프레임워크를 만들 수 있다.

인터페이스를 정적 펙터리 메서드의 반환 타입으로 사용

자바 8 이전에는 인터페이스에 정적 메서드 선언이 불가해서 (인스턴스화 불가인) 동반 클래스를 만들어 정의 했다고 한다.

예시 중 하나가 Collections 다.

Collections 클래스는 자주 사용되는 정적 메소드(static methods)를 제공하는 유틸리티 클래스다.

객체 생성 없이 바로 접근 가능하며 유틸리티성 함수와 상수값들을 제공한다.

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}// public static final List EMPTY_LIST = new EmptyList<>();
// 비어있는 리스트 구현체를 반환
List<String> emptyList = Collections.emptyList();

  • 4. 입력 매개변수에 따라 매번 다른 클래스(반환타입의 하위타입)의 객체를 반환 가능
    • 아래 코드는 원소가
      • 64개 이하면 RegularEnumSet(원소들을 long 변수 하나로 관리)
      • 65개 이상이면 JumboEnumSet(long 배열로 관리) 반환
    • 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. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
    • 이 점을 활용해 만든 것이 서비스 제공자 프레임워크에서의 서비스 접근 API

👉🏻 서비스 제공자 프레임워크(Service provider framework)

프레임워크가 클라이언트를 서비스의 구현체(제공자(provider))로 부터 분리해준다.

서비스 제공자 프레임워크의 핵심 컴포넌트

  1. 서비스 인터페이스(service interface)
    : 구현체의 동작을 정의
  2. 제공자 등록 API(provice registration API)
    : 제공자가 구현체를 등록할 때 사용
  3. 서비스 접근 API(service access API)
    : 클라이언트가 서비스의 인스턴스를 얻을 때 사용
    • 서비스 제공자 프레임워크의 근간인 유연한 정적 팩터리의 실체
      조건을 명시하지 않으면 기본 구현체를 반환하거나 지원하는 구현체들을 돌아가면서 반환한다.
  4. 서비스 제공자 인터페이스(service pro-vider interface)
    : 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체를 설명
    • 없으면 구현체를 인스턴스로 만들 때 리플렉션을 사용해야한다.

JDBC(JavaDatabase Connectivity) (java 5 이전)

대표적인 서비스 제공자 프레임워크

  • Connection: 서비스 인터페이스
  • DriverManager.registerDriver: 제공자 등록 API
  • DriverManager.getConnection: 서비스 접근 API
  • Driver: 서비스 제공자 인터페이스

DI(dependency injection) 프레임워크도 서비스 제공자 프레임워크

java.util.ServiceLoader: 범용 서비스 제공자 프레임 워크 (java 5부터)


단점

  1. 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다. (제약)
    상속을 하려면 public이나 protected 생성자가 필요하기 때문이다.
  2. 정적 팩터리 메서드는 찾기 힘들다.
    • API설명에 명확히 드러나지 않아 정적 팩터리 메서드 방식 클래스를 인스턴스화할 방법을 알아내야 한다.
728x90
LIST