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

최선욱·2021년 6월 21일
0

effective-java

목록 보기
1/1
post-thumbnail

Effective Java 3/E를 정리한 내용입니다. 제가 이해한 내용을 바탕으로 정리한 것이기 때문에 틀린 부분이 있을 수도 있습니다. 틀린 부분 댓글로 말씀해주시면 감사할 것 같습니다 :)

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

클라이언트가 클래스의 인스턴스를 얻는 전통적인 수단은 [public + class이름] 형태의 생성자입니다. 이런 public 생성자 외에 정적 팩터리 메서드를 통해 인스턴스를 생성하는 방법은 한번쯤 고려해보는 것이 좋습니다.

정적 팩터리 메서드란?

말 그대로 생성하고자 하는 인스턴스를 반환해주는 static method를 말합니다. 예를 들면 다음 예시처럼 boolean 원시타입 파라미터로 받아 Boolean 객체 참조를 반환해주는 static method를 말합니다.

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

그렇다면 왜 이 정적 팩터리 메서드를 왜 한번쯤 고려해봐야하는 걸까요? 그 이유는 다음과 같습니다.

정적 팩터리 메소드의 장점
1. 이름을 가질 수 있다.
2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

1. 이름을 가질 수 있다.

생성자는 클래스 이름을 사용해야해서 인스턴스의 특성을 제대로 설명 못하지만, 정적 팩터리 메서드를 사용하면 생성되는 인스턴스의 특성이 명확하게 설명할 수 있습니다. 예를 들어 소수인 BigInteger 인스턴스를 반환해주는 생성자와 정적 팩터리 메서드가 있습니다.

1. BigInteger(int, int, Random) 	// 생성자
2. BigInteger.probablePrime		// 정적 팩터리 메서드

1번의 경우는 BigInteger임을 알 수는 있지만 값이 소수인지 아닌지 코드만 보고 유추하기는 힘듭니다. 하지만 2번의 경우는 '값이 소수인 BigInteger를 반환한다.' 라는 의미를 더 쉽게 받아드릴 수 있습니다. 따라서 이렇게 인스턴스의 특성을 잘 표현해야한다면 정적 팩터리 메소드를 고려해보는 것이 좋습니다.

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

만약 생성자를 사용한다면 new 연산자를 사용해 매번 인스턴스를 생성해야합니다. 하지만 정적 팩터리 메서드를 사용하면 인스턴스를 따로 생성하지 않거나 이미 생성된 인스턴스를 참조하도록 하거나 하는 등 여러 종류의 인스턴스 통제가 가능합니다. 따라서 인스턴스를 통제해야하는 경우 정적 팩터리 메서드를 고려해보면 좋습니다.

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

클래스에서 생성할 인스턴스의 클래스를 선택할 수 있는 유연성을 가질 수 있습니다. 생성자에서는 자신을 인스턴스로 생성해야하지만 정적 팩터리 메서드에서는 하위 타입의 객체를 인스턴스로 반환하도록 하면 되기 때문에 생성자보다 더 유연한 구조를 가질 수 있습니다. 또한 하위 클래스에 대해서 자세히 몰라도 인터페이스를 통해 개발이 가능하여 개발자 입장에서는 부담이 덜어집니다.

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

3번과 같은 이유에서 정적 팩토리 메서드는 여러 유형의 인스턴스를 생성할 수 있습니다. 따라서 매개변수에 따라 다른 클래스의 인스턴스를 반환할 수 있도록 구현할 수 있습니다.
대표적인 예시로 EnumSet이 있습니다. EnumSet은 매개변수가 원소가 64개 이하면 RegularEnumSet 인스턴스를, 매개변수가 65개 이상이면 JumboEnumSet을 반환합니다.
이렇게 구현한다면 개발자는 RegularEnumSet, JumboEnumSet이 존재하는지조차 몰라도 됩니다. 단지 EnumSet 인터페이스를 사용할 뿐입니다. 따라서 후에 RegularEnumSet의 장점이 없어져 모두 JumboEnumSet으로 통일하거나 더 좋은 성능의 클래스를 추가하여도 크게 문제가 되지 않습니다.

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

3, 4번과 같은 개념의 장점입니다. 어떤 인스턴스를 만들어야할 지 모를 때 정적 팩터리 메서드를 사용하면 좀 더 유연하게 인스턴스를 생성할 수 있습니다. 예를 들면 JDBC에서 getConnection()을 할 때 어떤 디비를 쓰냐에 따라 드라이버가 달라지고 인스턴스가 달라지는 데 이 때 이 인스턴스를 유연하게 생성할 수 있습니다.

하지만 항상 정적 팩토리 메서드가 장점만 존재하는 것은 아닙니다.

단점
1. public이나 protected 생성자 없이 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.
2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.

1. public이나 protected 생성자 없이 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.

당연하게도 생성자가 존재하지 않으면 클래스를 상속받을 수 없습니다. 따라서 정적 팩토리 메서드만 존재하는 클래스는 하위 클래스를 구현할 수 없습니다. 이러한 제약사항은 단점이기도 하지만 상속이 아닌 컴포지션을 강제해 클래스간 결합도를 낮추기 떄문에 장점으로 받아드릴 수도 있습니다.

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

자바 Docs를 보면 생성자는 따로 정의하여 설명하지만 정적 팩토리 메서드는 설명이 명확하게 들어나지 않아 개발자가 인스턴스화하는 방법을 알아야합니다. 따라서 API 문서를 잘 써놓거나 이미 알려진 정적 팩토리 메서드 규약을 따라 짓는 방법으로 이러한 문제를 완화시켜줘야합니다.

핵심 : 정적 팩터리 메서드는 항상 장점만 있는 것이 아니기 때문에 public 생성자와 정적 팩터리 메서드의 쓰임새와 장단점을 잘 이해하고 사용하는 것이 좋습니다.

참고


0개의 댓글