본문 바로가기
개발/Effective Java

[Effective Java] 생성자 대신 정적 팩터리 메서드를 고려하라.

by 손너잘 2021. 12. 16.

장점

  • 이름을 가질 수 있다.

 좋은 장점이라고 생각한다. 생성자의 매개변수만으로는 생성되는 객체의 의미를 파악하기 힘든 경우가 분명 있다. 

하지만 이름을 가지면 좋다는 이유로 가끔 무분별하게 기본적인 생성자 호출로도 가능한 로직을 정적 팩터리 메서드를 사용하는 경우가 있다. 예를들면 아래와 같은 경우다.

public class Main {
	public static void main(String[] args) {
    	Calculator calc = Calculator.createNewCalculator();
        ...
    }
}

public class Calculator {
	...
    
	public static Calculator createNewCalculator() {
    	return new Calculator();
    }
    
    ...
}

 위와 같은 경우는 생성자를 이용하더라도 충분히 의미 전달이 된다. 또한 정적 팩터리 메서드에서 캐싱이라던지, 공변 타입 변환이라던지, 생성자에서 할 수 없는 뭔가 특별한 행위를 하고 있지도 않는다. 하지만 이러한 코드를 상당히 많이 보았다.

개인적으로 이러한 코드는 코드의 길이만 증가시킬 뿐 가독성면으로도, 성능적으로도 어떤 이득도 가져다 주지 않는다고 생각한다.

 

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

 꽤 좋은 장점이라고 생각한다. 생성자를 이용한 인스턴스화에서는 절대로 불가능한 캐싱과 같은 기능을 제공해주기 때문이다. 이를 통해 대규모 트래픽이 들어왔을 때 비즈니스 로직을 수행하면서 한번쓰고 버려질 객체들의 생성비용을 상당히 줄일 수 있을것이다. (마찬가지로 GC 비용또한 작아질 수 있을것이다.)

 

  • 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

이번에 이팩티브자바를 다시 읽으면서 새롭게 이해한 내용이다. 또한, 정적 팩터리 메서드를 클래스에서만 사용한다는 선입견을 완전히 사라지게 해줬다.

자바 8로 넘어오면서 인터페이스에 정적 메서드를 만들어 줄 수 있다. 개인적으로 이 기능을 적절하게 사용할 경우를 찾지 못했는데(이미 사용중인 경우 제외) 좋은 예시를 찾은것 같아서 좋다.

public interface Food {
    static Food newMeat() {
        return new Beef();
    }
    
    static Food newSalad() {
        return new Salad();
    }
}

좋은 예시는 아니지만 위와같은 식으로 코드를 작성할 수 있다는 인사이트를 얻을 수 있었다.

즉, 구현체가 아닌 인터페이스에 의존하면서 그 인터페이스를 인스턴스화 시키는 느낌의 코드 작성.. 이를 통해 객체간의 의존성을 더욱 줄일 수 있지 않을까 생각해본다.

(근데 이럴꺼면 걍 enum쓰는게 좋지 않나?)

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

3번째 장점과 비슷한 내용인것 같다. 결국 공변반환 타이핑을 이용해서 입력 매개변수에 따라 적절한 구현체를 제공하겠다는 내용이다.

뭐, 당연히 좋은 내용이다. 크게 설명할 부분은 없는 것 같다.

예시를 생각해보면 자바9의 List.of() 시리즈들이 그 예시가 될 것 같다.

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

와우 이 내용은 정말 충격적이다. 결국, 우리가 사용하는 기본적인 API에도 이러한 원칙이 녹아있었던 것이다.

특히, 정적 팩터리 메서드를 이용해서 인스턴스화를 구현체의 책임으로 밀어넣어 버린다는점이 재미있다.

클라이언트가 해야 할 책임을 다른쪽으로 넘기는데 있어서 정적 팩터리 메서드를 사용한다... 아무리 생각해도 재미있는 발상이다.

 

단점

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

책에서는 상속보다 컴포지션을 사용하도록 유도하고 불변 타입으로 만들려면 이 제약을 지켜야 해서 장점으로 볼 수 있다고 말한다. 하지만 개인적으로는 컴포지션보다는 상속이 필요한 경우가 왕왕있다. 또한 불변타입은 생성자가 있더라도 충분히 지켜질 수 있다.

따라서 이 단점을 변호하기 위한 저자의 생각에는 크게 공감할 수 없다.

  • 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.

정말 절절히 느껴지는 단점중 하나이다. 다른 사람들의 코드를 보다보면 의미를 정확하게 한답시고 정적 팩터리 메서드의 네이밍을 길게 쓰는 경우를 많이 봤는데, 개인적으로 별로 좋아하지 않는 코딩 스타일이다. 처음보는 사람은 이게 정적 팩터리 메서드인지 파악하기 어려울 수 있으며 책에 나와있듯 검색도 힘들다.

따라서 책에서 제공하는 네이밍 컨벤션으로 최대한 의도를 표현하려고 노력하고, 정 안된다면 그때는 절절하게 네이밍 하는 방식을 사용하는것이 좋아보인다.

댓글