본문 바로가기
개발/JAVA

[JAVA] 생성자 대신 정적 팩토리 메소드를 고려해야 하는 이유!

by zuzuu 2022. 1. 12.
반응형

 

먼저 정적 팩토리 메소드가 뭔지 잘 모르겠으면 아래 포스팅을 보고 오자!

 

[Java] 정적 팩토리 메소드란? (static factory method)

정적 팩토리 메서드 정적(static), 팩토리(factory), 메서드(method) GoF라는 디자인 패턴 중 팩토리 패턴에서 유래한 단어로 객체를 생성하는 역할을 분리하겠다는 것을 말한다. (하지만 정적 팩토리 메

ynzu-dev.tistory.com

 

1. 이름을 가질 수 있다.

BigInteger 클래스를 예를 들어 보면 생성자인 BigInteger(int, int, Random)과 정적 팩토리 메서드인 probablePrime(int, Random) 중 후자가 메소드 명덕분에 '값이 소수인 BigInteger를 반환한다'라는 의미를 파악하기 쉽다.

BigInteger bigInteger = new BigInteger(int, int, Random);

public BigInteger(int bitLength, int certainty, Random rnd) {
        BigInteger prime;

        if (bitLength < 2)
            throw new ArithmeticException("bitLength < 2");
        prime = (bitLength < SMALL_PRIME_THRESHOLD
                                ? smallPrime(bitLength, certainty, rnd)
                                : largePrime(bitLength, certainty, rnd));
        signum = 1;
        mag = prime.mag;
}

BigInteger bigInteger = BigInteger.probablePrime(int, Random);

public static BigInteger probablePrime(int bitLength, Random rnd) {
        if (bitLength < 2)
            throw new ArithmeticException("bitLength < 2");

        return (bitLength < SMALL_PRIME_THRESHOLD ?
                smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
                largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
}

이렇게 정적 팩토리 메서드는 네이밍만 잘 짓는다면 직관적으로 표현할 수 있어서 엉뚱한 것을 호출하는 실수를 줄일 수 있다.

Boolean.logicalOr(boolean a, boolean b);
Boolean.logicalAnd(boolean a, boolean b);

 

반면 생성자는 생성자의 특성 파악이 필요하며, 시그니처가 동일한 것(메소드 명, 메소드 파라미터가 동일한 것)을 중복해서 사용할 수 없다. 

 

 

2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.

불변 클래스의 경우 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하고, 재활용하여 불필요한 객체 생성을 피할 수 있다. 

예를 들어 Boolean.valudOf(boolean) 은 객체를 아예 생성하지 않는다. 아래는 Boolean 클래스의 일부이다.

 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 Parent {

    public Parent(){}
    
    public static Parent getChildInstance() {
        return Child.getInstance();
    }
}

public class Child extends Parent{

    private Child(){}

    public static Child getInstance() {
        return new Child();
    }
}

반환할 객체 클래스를 자유롭게 선택할 수 있어 유연성이 제공된다.

이를 응용하면 API를 만들 때 구현 클래스(Child)를 공개하지 않고, Parent를 통해 반환할 수 있어서 API를 작게 만들 수 있다. 

 

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

static class CARD{
    public static final String SAMSUNG_CARD = "삼성";
    public static final String KAKAO_CARD="카카오";
    
    static Payment payment(String card){ // 들어오는 card 의 값 에 따라 반환하는 객체가 다르다
        switch (card){
            case SAMSUNG_CARD:
                return new SamSungPayment();
            case KAKAO_CARD:
                return new KakaoPayment();
        }
        throw new IllegalArgumentException();
    }
}

이 부분은 설명하기 위한 예제가 떠오르지 않아 다른 사람의 포스팅에서 발췌하였다. (출처 : https://k3068.tistory.com/

SamSungPayment와 KakaoPayment 클래스는 Payment 클래스를 상속받은 상태이다.

 

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

이런 유연함으로 JDBC와 같은 서비스 제공자 프레임워크를 만드는 근간이 된다.  JDBC 드라이버를 로딩하고 Connection을 생성하는 소스를 보면 아래와 같다.

//JDBC 드라이버 로딩 
Class.forName("com.mysql.jdbc.Driver"); 

//Connection 생성 
Connection con = DriverManager.getConnection(url, user, pw); //DB 접속 정보

getConnection()에 의해 반환되는 구현체는 DBMS의 종류에 따라 달라지게 되는데, 정적 팩토리 메서드를 이용하면 각 DBMS에 맞는 API를 따로 구현하지 않고 클래스가 로드되는 시점에서 구현체를 생성해서 반환한다.

참고 : Class.forName() 에 대한 포스팅  -> https://kyun2.tistory.com/23

 

 

하지만 장점만 있는 것이 아니라 단점도 있다.

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

 

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

생성자처럼 API 설명에 명확히 드러나지 않으므로, 널리 알려진 명명 규칙에 따라 메소드 명을 네이밍 해야할 것 이다.

 

결론

정적 팩토리 메서드와 생성자는 각각의 쓰임새가 있으니 각각의 장단점을 파악하여 상황에 따라 사용하면 될 것 같다.

그래도 정적 팩토리 메서드가 유리한 경우가 더 많으므로 무조건 생성자를 사용하던 습관을 고쳐야 한다고 한다..! 

 

 

출처 : 이펙티브자바

'생성자 대신 정적 팩토리 메서드를 고려하라'

 

 

728x90
반응형

댓글