1. 생성자 대신 정적 팩터리 메서드
클래스는 생성자와 별도로 정적 팩터리 메서드(static factory method) 제공
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
<주의> 팩터리 메서드 디자인 패턴을 말하는 것이 아님
생성자보다 정적 팩터리 메서드가 좋은 장점
- 이름을 가질 수 있다.
- 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
- Immutable 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱해 재활용하는 식으로 불필요한 객체 생성을 피할 수 있음
- 반환 타입의 하위 타입 객체를 반환할 수 있다.
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
단점
- 상속하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
- 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
정적 팩터리 메서드에 흔히 사용하는 명명 방식들
- from: 매개변수 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
Date d = Date.from(instant);
- of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- valueOf: from과 of의 더 자세한 버전
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- getInstance: 매개변수로 명시한 인스턴스를 반환하지만 같은 인스턴스임을 보장하지 않음
StackWalker luke = StackWalker.getInstance(options);
- create(newInstance): 매번 새로운 인스턴스를 생성해 반홤함을 보장
Object newArray = Array.newInstance(classObject, arrayLen);
- getType: getInstance와 같으나 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용
FileStore fs = Files.getFileStore(path);
- newType: newInstance와 같으나 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용
BufferedReader br = Files.newBufferedReader(path);
- type: getType, newType의 간결한 버전
List<Complaint> litany = Collections.list(legacyLitany);
2. 생성자에 매개변수가 많다면 빌더 고려
정적 팩터리와 생성자는 선택적 매개변수가 많을때 적절히 대응하기 어렵다는 제약이 있다.
단점의 대안으로 사용된 예시들
- 점층적 생성자 패턴(telescoping constructor pattern): 필수 매개변수만 받는 생성자, 필수 매개변수와 선택 매개변수 n개를 받는 생성자를 모두 선언하는 방식
- 매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기가 어려움
- 자바빈즈 패턴(JavaBeans pattern): 매개변수가 없는 생성자로 객체를 만든 후 setter 메서드들을 호출해 필요한 매개변수의 값을 설정하는 방식
- 객체 하나를 만들려면 메서드를 여러 개를 호출해야하고 객체가 완전히 생성되기 전까지는 일관성이 깨진 상태에 놓임 -> 불변으로 만들 수 없음
- 빌더 패턴(Builder pattern): 클라이언트는 필요한 객체를 직접 만드는 대신 필수 매개변수만으로 생성자, 정적 팩토리를 호출해 빌더 객체를 얻고, 빌더 객체가 제공하는 수정자 메서드들로 선택 매개변수들을 설정 후 build 메서드 호출하는 방식
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbonhydrate(27)
.build();