규칙1 생성자 대신 정적 팩터리 메서드를 사용할 수 없는지 생각해 보라
객체의 생성과 삭제
- 객체를 만들어야 하는 시점과 그 방법
- 객체 생성을 피해야 하는 경우와 그 방법
- 적절한 순간에 객체가 삭제되도록 보장하는 방법
- 삭제 전에 반드시 이루어져야 하는 청소 작업들을 관리하는 방법
규칙1 생성자 대신 정적 팩터리 메서드를 사용할 수 없는지 생각해 보라
- 기본 ( primitive ) 타입의 b를 객체형 Boolean으로 반환한다.
- 참조 ( reference ) 로 반환한다.
Public static Boolean valueOf ( boolean b ) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
즉, Public 생성자 대신 객체 생성이 가능한 방법이 하나 더 있다. 또한, public으로 선언된 생성자 대신에 정적 팩터리 메서드를 제공하는 방법의 장단점은 다음과 같다.
첫 번째, 생성자와는 달리 정적 팩터리 메서드에는 이름 ( name )이 있다.
- 생성자에 전달되는 인자들은 어떤 객체가 생성되는지를 설명하지 못한다.
- 정적 팩터리 메서드는 이름을 잘 짓기만 한다면 사용하기도 쉽다.
- 클라이언트 코드의 가독성 ( readability )도 높아진다.
예를 들어 소수 ( prime )일 가능성이 높은 BigInteger 객체를 생성하는 생성자 BigInteger(int, int, Random)는 BigInteger.probablePrime과 같은 이름의 정적 팩터리 메서드로 표현 했으면 더 이해하기 쉬웠을 것이다.
또한, 클래스에는 시그니처 ( signature ) 별로 하나의 생성자만 넣을 수 있다. 이 제약을 피하는 한 가지 방법은 인자의 순서를 바꾸는 것이다. 하지만, 이 방법은 정말로 끔찍하다. API 사용자는 각각의 생성자 용도를 절대로 기억하지 못할 것이다. 또 결국 실수로 엉뚱한 생성자를 호출하게 될 것이다. 반면 정적 팩터리 메서드에는 이름이 있으므로 그런 문제는 생기지 않는다.
두 번째, 정적 팩터리 메서드는 생성자와는 달리 호출할 떄마다 새로운 객체를 생성할 필요는 없다는 것이다.
- 미리 만들어 둔 객체의 재활용
- 미리 만들어 둔 객체를 캐시해 놓고 재사용
앞서 살펴본 Boolean.valueOf(Boolean) 메서드가 이 기법을 활용한 좋은 사례다. 결코, 객체를 생성하지 않는 것이다. 이 기법은 경량 ( Flyweight ) 패턴과 유사한 사례다. 동일한 객체가 요청되는 일이 잦고, 특히 객체를 만드는 비용이 클 떄 적용하면 성능을 크게 개선할 수 있다.
정적 팩터리 메서드를 사용하면 같은 객체를 반복해서 반환할 수 있으므로 어떤 시점에서 어떤 객체가 얼마나 존재할지를 정밀하게 제어할 수 있다. 그런 기능을 갖춘 클래스는 개체 통제 클래스 ( Instance-Controlled Class )라고 부른다.
개체 통제 클래스를 작성하는 이유
- 개체 수를 제어하면 싱글톤 ( Singleton ) 패턴을 따르도록 할 수 있다.
- 객체 생성이 불가능한 클래스를 만들 수 있다.
- 변경이 불가능한 클래스의 경우 두 개의 같은 객체가 존재하지 못하도록 할 수도 있다. ( a == b 일 때만 a.equals(b)가 참이 되도록 할 수 있다. )
- 이렇게 구현된 클래스는 == 연산자를 이용해 비교할 수 있으므로 성능이 향상된다. ( 열거(Enum) 자료형이 이 기법을 사용한다. )
세 번째, 정적 팩터리 메서드는 생성자와는 달리 반환값 자료형의 하위 자료형 객체를 반환할 수 있다는 것이다.
- 반횐되는 객체의 클래스를 훨씬 유연하게 결정할 수 있다.
- 이 유연성을 활용해 Public으로 선언되지 않은 클래스의 객체를 반환하는 API를 만들 수 있다. ( 구현 세부사항을 감출 수 있다. )
- 이 기법은 인터페이스 기반 프레임워크 ( Interface-based Framework ) 구현에 적합하다.
Public 정적 팩터리 메서드가 반환하는 객체의 클래스가 Publicd일 필요가 없다.
메서드에 주어지는 인자를 이용하면 어떤 클래스의 객체를 만들지도 동적으로 결정할 수 있다.
반환될 객체의 클래스가 정적 팩터리 메서드의 반환값 자료형에 부합하기만 하면 된다.
릴리스 되는 소프트웨어의 버전에 따라서 반환될 객체의 클래스 구현을 달리할 수 있으므로 소프트웨어를 유지보수하거나 성능을 개선하기도 좋다.
JDK 1.5에 도입된 java.util.EnumSet에는 public으로 선언된 생성자가 없고 정적 팩터리 메서드뿐이다.
이 메서드들은 Enum 상수 개수에 따라 두 개 구현체 가운데 하나를 골라 해당 클래스의 객체를 만들어 반환 한다.
Enum 상수들이 64개 이하일 경우 RegularEnumSet을 반환하는데 내부적으로 long형 변수 하나만 사용한다.
64개 이상일 경우 JumboEnumSet을 반환하는데 이 객체는 내부적으로 long형 배열을 사용한다.
정적 팩터리 메서드만 있는 클래스를 만들면 생기는 가장 큰 문제는, public이나 protected로 선언된 생성자가 없으므로 하위 클래스를 만들 수 없다는 것이다. 두 번째 단점은 정적 팩터리 메서드가 다른 정적 메서드와 확연히 구분되지 않는다는 것이다.
valueOf, of, getInstance, newInstance, getType, newType
출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 2장 인용.