Item1 : 생성자 대신 정적 팩토리 메서드를 고려하라
클래스의 인스턴스를 얻는 가장 기본적이고 전통적인 방법은 public 생성자이다.
new 키워드를 이용하여 인스턴스를 생성할 수 있다.
Item item = new Item();
이 방법과는 별도로 정적 팩토리 메서드(static factory method)를 제공하는 방법을 꼭 알아두면 좋다.
해당 클래스의 인스턴스를 반환하는 단순한 정적 메서드이다.
예를 들면 아래와 같이 primary type인 boolean의 boxing class Boolean에서 정적 팩토리 메서드를 이용해서 boolean을 Boolean으로 변환하는 메서드가 있다.
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
이렇게 생성자 대신 정적 팩토리 메서드를 사용하면 여러 가지 장단점이 존재하지만, 대부분의 경우 정적 팩토리를 사용하는 게 유리한 경우가 더 많기 때문에 무작정 public 생성자를 제공하던 습관이 있다면 고치는 게 좋다.
장점
1. 이름을 가질 수 있다.
생성자는 이름을 가질 수 없고, 매개변수의 개수나 타입으로 구별할 수 있다.
그러나 정적 팩토리 메서드의 이름을 잘 지으면 각 메서드가 어떤 역할을 하는지 의미를 명확하게 할 수 있다.
2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
불변 클래스의 경우에는 인스턴스를 미리 만들어놓거나 인스턴스를 캐싱하여 재활용하는 방식으로 불필요한 객체 생성을 막을 수 있다. 위에서 예로 들었던 Boolean.valueOf(boolean b) 메서드는 객체를 아예 생성하지 않는다.
3. 반환 타입의 하위 타입 객체를 반환할 수 있다.
메서드의 매개변수에 따라 반환 타입의 자식 클래스를 반환할 수 있다. 즉, 반환할 객체의 클래스를 자유롭게 선택할 수 있는 유연성을 가질 수 있게 된다.
4. 매개변수에 따라 다른 클래스의 객체를 반환할 수 있다.
3번과 연결되는 내용이다. 매개변수의 개수나 타입, 값 등에 따라 다른 객체를 반환하도록 할 수도 있다.
5. 정적 팩토리 메서드를 작성하는 시점에서 반환할 객체의 클래스가 존재하지 않아도 된다.
이 부분은 정확히 이해가 되지는 않지만, 확장성에 관련된 내용으로 생각된다.
단점
1. 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.
상속을 하려면 public이나 protected 생성자가 필요하다.
만약 정적 팩토리 메서드로만 생성을 하도록 막아두려면, 생성자를 private으로 막아야 하기 때문에 상속을 받을 수 없다.
2. 정적 팩토리 메서드는 프로그래머가 찾기 어렵다.
생성자는 기본적으로 해당 클래스의 맨 위쪽에 위치시킨다.
정적 팩토리 메서드는 일반적인 메서드 형태이기 때문에 프로그래머가 이게 생성을 위한 정적 팩토리 메서드라는 사실을 알아차리기 어렵다는 뜻이다.
큰 단점은 존재하지 않는 것 같다.
1번의 경우에는 상황에 따라 기본 생성자만 public으로 열어두는 등 상황에 맞게 사용하면 될 것 같다.
2번의 경우는 주석이나 API 문서를 잘 써서 해결할 수 있을 것 같다.
메서드명의 일반적인 규칙들
from
매개변수 하나를 받아서 해당 타입의 인스턴스로 반환
Date d = Date.from(instant);
of
여러 매개변수를 받아 적합한 타입의 인스턴스를 반환
List<String> list = List.of("i1", "i2", "i3");
valueOf
from과 of의 더 자세한 버전
Integer i = Integer.valueOf("1");
instance / getInstance
매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지는 않는다.
StackWalker luke = StackWalker.getInstance(options);
create / newInstance
instance, getInstance와 같지만, 매번 새로운 인스턴스를 생성해 반환함을 보장한다.
Integer[] arr = (Integer[]) Array.newInstance(Integer.class, 3); //[null, null, null]
getType
getInstance와 같지만, 생성할 클래스가 아닌 다른 클래스에 메서드를 정의할 때 쓴다.
메서드 명의 "Type" 대신 팩토리 메서드가 반환할 객체의 타입을 쓴다.
FileStore fs = Files.getFileStore(path);
newType
newInstance와 같지만, 생성할 클래스가 아닌 다른 클래스에 메서드를 정의할 때 쓴다.
메서드 명의 "Type" 대신 팩토리 메서드가 반환할 객체의 타입을 쓴다.
BufferedReader br = Files.getBufferedReader(path);
type
getType과 newType의 간결한 버전
List<Item> items = Collections.list(itemList);
실제 사용 예시
ItemInput의 값을 바탕으로 Item을 생성하는 정적 팩토리 메서드이다.
public class Item {
private Long itemId;
private String name;
private int price;
private LocalDateTime createdAt;
public static Item fromInput(ItemInput itemInput){
Item item = new Item();
item.setName(itemInput.getName());
item.setPrice(itemInput.getPrice());
item.setCreatedAt(LocalDateTime.now());
return item;
}
}
'기타 > Book Review' 카테고리의 다른 글
[Effective Java] Item 15-17 : 클래스의 접근 권한 설정과 불변 클래스 (1) | 2024.03.19 |
---|---|
[CleanCode] 클린코드 리뷰_7장 : 오류처리 (0) | 2023.06.24 |
[CleanCode] 클린코드 리뷰_6장 : 객체와 자료구조 (0) | 2023.06.21 |
[Clean Code] 클린코드 리뷰_ 5장 : 형식 맞추기 (1) | 2023.06.17 |
[CleanCode] 클린코드 리뷰_ 4장 : 주석 (0) | 2023.06.16 |