[아이템1] 생성자 대신 정적 팩터리 메서드를 고려하라

2023. 4. 27. 13:42Effective Java

반응형

생성자와 정적 팩터리 메소드의 공통적인 역할: 객체를 생성한다.

<정적 팩터리 메소드의 장점>

1. 이름을 가질 수 있다.

객체는 생성 목적에 따라 사용할 필요가 있다.

생성자는 내부 구조를 잘 알고 있어야 목적에 맞게 객체를 생성할 수 있다.

하지만 정적팩터리 메서드를 사용하면 메서드 이름에 객체의 생성 목적을 담아낼 수 있다. -> 가독성이 좋아진다.

//생성자
public BigInteger(int bitLength, int certainty, Random rnd){}

//정적 팩터리 메소드
public static BigInteger probablePrime(int bitLength, Random rnd){}

정적 팩터리 메소드는 값이 소수인 BigInteger를 반환하는 메소드라고 알 수 있음.

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. 하위 자료형 객체를 반환할 수 있다.

리턴하는 객체의 타입을 유연하게 지정할 수 있다. 또한 이를 응용하면 API를 만들 때 구현클래스(하위)를 공개하지 않고 상위 클래스를 통해 반환할 수 있어서 API를 작게 만들 수 있다.

static class CARD{
 public static final String SAMSUMG_CARD="삼성";
 public static final String KAKAO_CARD="카카오";

 static Payment payment(String card){
  switch(card){
   case SAMSUNG_CARD:
    //반환 타입의 하위타입 객체를 반환할 수 있는 능력이 있다.
    return new SamSungPayment();
   case KAKAO_CARD:
    return new KakaoPayment();
  }
  throw new IllegalArgumentException();
 }
}

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

반환타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다. 위의 "하위 자료형 객체를 반환할 수 있다."의 연장선인데, 위 예시의 payment의 매개변수인 card의 값에 따라, 반환되는 클래스들이 다르다.

5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도된다.

이런 유연함은 서비스 제공자 프레임워크를 만드는 근간이 된다.

ex)JDBC

서비스 제공자 프레임워크는 3개의 핵심 컴포넌트로 구성된다.

1)서비스 인터페이스 -> 구현체의 동작을 정의

2)서비스 등록 API -> 구현체를 시스템이 등록하여 클라이언트가 쓸 수 있도록 함

3)서비스 접근 API -> 클라이언트에게 실제 서비스 구현체를 제공

Class.forName(driverName); -> 드라이버의 이름을 호출하기만 했는데 서비스를 이용할 수 있음.

=>자바에서는 제공하는 Driver 클래스가 클래스로더에 의해 로드되면 Class.forName(String driverName)에서 파라미터로 받은 driverName에 해당하는 클래스를 로딩하며 자체적으로 인스턴스를 만들어 DriverManager클래스에 등록(DriverManager.registerDriver, 서비스 등록 API)이 된다.

즉, 동적으로 현재 코드가 실행되는 클래스 로더에 해당 클래스(여기서는 driverName)들을 로딩하고 그 클래스의 인스턴스를 생성해서 반환할 수 있다는것이다.

Connection conn = DriverManager.getConnection(url, user, password);

DriverManager를 통해 커넥션을 얻어오는부분 -> 서비스 접근 API

클라이언트는 서비스 접근 API를 통해 Connection(서비스 인터페이스)을 얻고, 이를 이용하여 Statement를 생성하여 SQL을 실행하고 결과를 얻을 수 있다.

<정적 메소드의 단점>

1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메소드만 제공하면 하위 클래스를 만들 수 없다.

정적 팩토리 메소드의 접근제어자가 private이므로, 이 클래스는 누군가의 부모 클래스가 될 수 없다.

2. 프로그래머가 찾기 어렵다.

생성자는 javadoc이 알아서 정리해주는데 정적팩토리 메소드는 그런걸 안해준다. 그러므로 API문서를 잘 작성하고 메서드 이름도 널리 알려진 규약을 따라 짓는 식으로 문제를 완화해줘야한다.

반응형