ITEM 01. 생성자 대신 정적 팩터리 메서드를 고려하라
장점
- 이름을 가질 수 있다.
- 이름으로 반환 될 객체의 특성을 나타낼 수 있다.
- 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
- 같은 객체 요청 상황에 생성 비용이 큰 경우 성능을 높인다.
- 하위 클래스 타입을 반환할 수 있는 유연성이 있다.
- 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
- 가령 EnumSet 클래스에서는 매개변수의 원소 수에 따라 두 가지 하위 클래스 중 하나의 인스턴스를 반환한다.
- 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
- 정적 팩토리 메서드를 활용하면, 반환할 객체의 클래스가 컴파일 타임에 존재하지 않아도 된다. 특정한 하위 타입이 필요한 런타임 시점 때, 정적 팩토리 메서드 내에서 찾을 수만 있으면 된다.
단점
- 상속을 하기 위해서는 public이나 protected 생성자가 필요하다.
- 정적 팩터리 메서드는 프로그래머가 찾기 어렵다. 널리 알려진 규약을 따라 짓는 식으로 문제를 완화해줘야 한다.
- from: 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환
- Date date = Date.from(Instant.now());
- of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
- List
list = List.of(1, 2, 3);
ITEM 02. 생성자에 매개변수가 많다면 빌더를 고려하라
(선택적) 매개변수가 많을 때 매개변수에 따라 생성자를 만들어 가는 방식은 코드를 읽기 어렵다. 자바빈즈 패턴을 사용하면 객체가 완성되기 전까지 일관성이 무너지고 클래스를 불변으로 만들 수 없다.
- 자바빈즈 패턴: 매개변수 없는 생성자로 객체 생성 후, setter를 통해 매개변수의 값을 설정하는 방식
빌더 패턴을 사용하면 읽기 쉬워 가독성을 높일 수 있으며 가변 매개변수를 활용함으로써 유연함을 얻을 수 있다.
ITEM 03. private 생성자나 열거 타입으로 싱글턴임을 보증하라.
싱글턴이란 인스턴스를 오직하나만 생성할 수 있는 클래스를 말한다. 싱글턴을 만드는 방식은 보통 둘 중 하나다. 두 방식 모두 생성자는 private으로 감춰둔다. 첫 번째 방법은 유일한 인스턴스에 접근할 수 있는 수단으로 public static final 필드를 갖는다.
public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private Elvis(){};
}
두 번째 방법은 정적 팩터리 메서드를 public static 멤버로 제공한다.
public class Elvis{
private static final Elvis INSTANCE = new Elvis();
private Elvis(){};
public static Elvis getInstance(){
return INSANCE;
}
}
세 번째 방식으로는 원소가 하나인 열거 타입을 선언하는 것이다.
public enum Elvis{
INSTANCE;
public void foo(){};
}
public 필드 방식과 비슷하지만 추가 노력 없이 직렬화할 수 있다. 그리고 리플렉션 공격에서도 제2의 인스턴스가 생기는 일을 완벽히 막아준다.
ITEM 04. 인스턴스화를 막으려거든 private 생성자를 사용하라
정적 메서드나 정적 필드만을 갖는 클래스를 생성하는 경우가 있다. 객체 지향적 관점에서 그렇게 좋지는 않은 방식이지만 분명 필요한 경우가 있는데, 예를 들면 유틸성 클래스이다. 이러한 클래스들은 인스턴스를 생성하기 위해 만든 것이 아니다. 하지만 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 만들 것이고, 다른 사용자는 이것이 자동 생성된 것인지 구분할 수 없을 것이다. 그러므로 이를 방지하기 위해 private 생성자를 추가해주도록 하자. 이 방식은 상속을 불가능하게 하는 효과도 있다.
ITEM 05. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
의존 객체 주입은 필요한 자원을 생성자에 넘겨주는 것을 말한다. 의존 객체 주입은 클래스의 유연성, 재사용성, 테스트 용이성을 개선한다.
ITEM 06. 불필요한 객체 생성은 피하라
불변 객체는 언제든 재사용할 수 있기 때문에 똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다.
정규표현식용 Pattern 인스턴스는 생성 비용이 높고 한 번 쓰고 곧바로 가비지 컬랙션 대상이 된다.
static boolean isRomanNumeral(String s){
return s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$");
}
성능이 중요한 상황에서 반복해 사용할 때는 static 초기화 과정에서 직접 생성해 인스턴스를 재사용하면 성능을 개선할 수 있다.
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$");
static boolean isRomanNumeral(String s){
return ROMAN.matcher(s).matches();
}
}
오토박싱으로 인해 불필요한 인스턴스가 생성될 수 있으니 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의해야 한다.
이번 아이템은 "객체 생성은 비싸니 피해야 한다"로 오해하면 안된다. "기존 객체를 재사용해야 한다면 새로운 객체를 만드리지 마라" 를 의미한다.
'공부방 > Effective Java' 카테고리의 다른 글
[Effective Java] 09.일반적인 프로그래밍 원칙 (0) | 2024.08.10 |
---|---|
[Effective Java] 04.클래스와 인터페이스 (0) | 2024.08.10 |
[Effective Java] 03.모든 객체의 공통 메서드 (0) | 2024.08.10 |