Language/Java

Effective Java - 생성자 대신 정적 팩토리 메서드를 사용하라!

JaeHoney 2022. 1. 2. 11:58

정적 팩토리 메서드

저는 클래스를 작성할 때 객체를 인스턴스화 하는 코드를 작성할 때는 무조건 생성자를 사용했었어요!

 

Effective 자바의 내용 중에 생성자 대신 정적 팩토리 메서드를 사용하는 것을 검토해봐야 한다는 논의가 있었는데 좋은 내용 같아서 가지고 왔어요!

 

팩토리 메서드란 객체를 생성하는 메서드를 말합니다!

정적 팩토리 메서드는 객체를 생성하지 않고, 정적 메서드를 활용해서 객체를 만드는 방법이겠네요 !

정적 팩토리 메서드를 사용하는 이유

 

1. 이름을 가질 수 있다.

생성자는 클래스명을 이름으로 하고, 클래스 하나당 생성자 1개 밖에 둘 수 없기에 어떤 객체를 생성하는 지 직관적이지 않습니다. 

public class Car{
    String brand;
    String color;

    public Car(){
    }

    public Car(String brand){
        this.brand = brand;
    }

    public Car(String brand, String color){
        this.brand = brand;
        this.color = color;
    }
}

여기서 클래스를 확인하지 않고, 비즈니스 로직이 있는 코드에서 Car car = new Car("Red");를 접할 때, Red가 Brand인지 Color인지 알 수 있는 방법은? 없습니다. 파라미터에 대한 정보를 일일이 구해야 합니다.

Car car = new Car("Red");

 

반면, 정적 팩토리 메서드는 여러 개를 각각의 이름으로 사용할 수 있습니다. 따라서, 파라미터에 대한 정보를 유추할 수 있게 해서 어떤 객체를 생성하는지 더 명확한 의미를 줄 수 있습니다.

public class Car{
    String brand;
    String color;

    public static newEmptyInstance(){
    }

    public newInstanceWithBrand(String brand){
        this.brand = brand;
    }
}

 

 

2. 인스턴스 생성을 줄일 수 있다.

싱글톤 기법의 장점은 아시다시피, 크기가 큰 동일한 인스턴스를 여러 개 생성하지 않고, 인스턴스를 하나만 생성하고 참조만 해서 메모리를 절약하는 것입니다. 여기서 말하는 부분도 비슷한 기법이라고 할 수 있습니다.

public class Car{
    private static final Car basicCar = new Car("matiz", "white");

    public static newBasicCar(){
        return basicCar;
    }
}

하지만 싱글톤 패턴의 경우 생성자가 하나밖에 없기 때문에, 한 가지 객체밖에 사용할 수 없지만, 정적 팩토리 메서드로 플라이웨이트 패턴을 사용하면 여러 가지 객체를 만들어서 각각의 싱글톤 패턴을 하나의 클래스에서 구축할 수 있습니다.

 

3. 다른 클래스의 객체를 반환 할 수 있다.

생성자는 당연히 본인 클래스의 객체밖에 반환하지 못합니다. 정적 팩토리 메서드를 사용하면 다른 클래스의 객체를 반환할 수 있습니다. 그래서 하위 클래스의 소스를 모르더라도 상위 클래스에서 여러 개의 하위 객체를 생성해서 관리할 수 있습니다.

public class Car{
    public static newInstance(String brand){
    	if(oilCarBrandSet.contains(brand)) new OilCar();
        else if(electricCarBrandSet.contains(brand)) new ElectricCar();
    }
}

 

4. 클래스의 구현체를 몰라도 된다.

Spring MVC에서 JDBC를 사용할 때, 우리는 클래스 구현체를 몰라도 되고, 생성자를 사용하지도 않습니다. 서비스 접근 API(Service access API)를 사용해서 정해진 규격대로 사용하게 되죠. 

public class ConnectionTest {
    public static void main(String[] args) {
        Connection conn = null;

        try{
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost/dev";
            conn = DriverManager.getConnection(url, "dev", "dev");
        }
        catch(ClassNotFoundException e){
            System.out.println("드라이버 로딩 실패");
        }
        finally{
            conn.close();
        }
    }
}

정적 팩토리메서드를 사용하면 객체 생성 -> 필드 주입 -> 메서드 실행 같은 틀에서 벗어나서 유연하게 설계할 수 있습니다 !

 

정적 팩토리 메서드의 단점

 

1. 상속을 할 수 없다.

자바는 내부적으로 상속을 하려면 생성자가 필요합니다. 생성자를 사용하지 않으면 상속을 할 수 없습니다. 

하지만 상속 대신 컴포지션을 사용하도록 유도할 수 있고, 이로 인해 얻는 점이 더 많아서 오히려 장점으로 받아들일 수도 있다고 하네요!

 

2. 정해진 패턴을 따르지 않는다.

생성자를 사용한다면 우리가 아는 생성자 사용법으로 인스턴스를 만들 수 있지만, 정적 팩토리 메서드를 사용하면 클래스의 객체를 어떻게 얻을 수 있는지를 참조해야 한다는 점입니다. 

 

메서드의 이름이 뭔지, 어떤 메서드인지 작성된 문서를 보거나 학습이 되어야만 한다는 것이 단점입니다!

 

 

결론

정적 팩토리 메서드를 사용하는 게 유리한 경우가 훨씬 많다고 하는데, 아직까지는 크게 공감가지는 않는 것 같습니다. 그렇지만, JDBC 예제에서 처럼 많이 사용되고 있는 것은 분명하고 상대적인 장단점이 존재합니다.

 

무작정 생성자를 사용하기보다는 꼼꼼히 검토해보고 적용할 수 있는 최선의 방안을 적용하는 것이 적잖은 성과로 연결될 수 있을 것 같아요!

 

 

내용이 조금 어려웠지만, 잘 봐주시고 부족한 부분은 댓글로 말씀해주시면 정말 감사하겠습니다 !