티스토리 뷰
728x90
반응형
생성자 대신 정적 팩토리 메서드를 고려하라.
- 클라이언트가 클래스의 인스턴스를 얻는 전통적인 수단은 public 생성자입니다. 하지만 전통적인 수단 이외에 클래스는 생성자와 별도로 정적 팩토리 메서드를 제공할 수 있습니다.
- 위의 말은 크게 와닿지 않습니다. 조금 더 쉽게 표현을 해보자면 우리(?)는 지금까지 new 키워드를 사용하여 인스턴스를 생성하곤 했습니다. 이 방법 외에도 static 메서드를 사용하여 생성자를 생성하고 반환할 수 있습니다.
그래서 static 메서드를 이용해서 생성자를 생성하고 반환하면 뭐가 좋을까?
💡이름을 가질 수 있습니다.
- 아래 예제는 new 키워드를 사용하여 객체를 생성하고 있습니다. new 키워드를 사용하여 객체를 생성하고 있지만 어떤 용도로 객체 생성되어 사용되는지 코드로만 추측하기 어려운 부분이 있습니다.
- 다음 예제에서는 정적 팩토리 메서드를 사용하여 객체를 생성하고 있습니다. static 메서드를 사용하여 객체를 생성 후 반환을 하고 있으며 메서드의 이름을 보고 객체의 생성 목적을 담아낼 수 있습니다.
// new 키워드를 사용하여 객체 생성
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class Member {
private Long id;
private String name;
private int age;
}
public class EffectiveJavaApplication {
public static void main(String[] args) {
Member member = new Member(1L, "홍길동", 25);
}
}
// 정적 팩토리 메서드를 사용하여 객체 생성
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
private Long id;
private String name;
private int age;
public static Member createMember(Long id, String name, int age) {
return Member.builder()
.id(id)
.name(name)
.age(age)
.build();
}
public static Member updateMember(Long id, String name) {
return Member.builder()
.id(id)
.name(name)
.build();
}
}
public class EffectiveJavaApplication {
public static void main(String[] args) {
Member member = Member.createMember(1L, "홍길동", 25);
}
}
💡호출될 때마다 인스턴스를 새로 생성하지 않아도 됩니다.
- 싱글톤 패턴에 대하여
- 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있습니다.
💡반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있습니다.
💡입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있습니다.
💡정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됩니다.
- 위 3가지 조건에 해당하는 예제를 살펴보겠습니다.
- SmartPhone 인터페이스를 사용하여 하위 타입의 객체를 생성할 수 있도록 하였습니다.
- EffectiveJavaApplication의 main() 메서드를 보면 입력 매개변수에 따라 객체의 생성이 달라집니다.
- SmartPhoneFactory 클래스를 사용하여 컴파일 시점이 아닌 런타임시에 객체를 생성하므로 작성하는 시점에 반환할 클래스가 존재하지 않아도 됩니다.
public interface SmartPhone {
SmartPhone init(String name, int version);
}
@ToString
@AllArgsConstructor
public class SamsungSmartPhone implements SmartPhone {
private String name;
private int version;
@Override
public SmartPhone init(String name, int version) {
return new SamsungSmartPhone(name, version);
}
}
@ToString
@AllArgsConstructor
public class AppleSmartPhone implements SmartPhone {
private String name;
private int version;
@Override
public SmartPhone init(String name, int version) {
return new AppleSmartPhone(name, version);
}
}
public class SmartPhoneFactory {
public static SmartPhone create(Type type) {
SmartPhone smartPhone = null;
if (type == Type.SAMSUNG) {
smartPhone = new SamsungSmartPhone("갤럭시", 6);
} else if (type == Type.APPLE) {
smartPhone = new AppleSmartPhone("아이폰 미니", 12);
}
return smartPhone;
}
}
public enum Type {
SAMSUNG,
APPLE
}
public class EffectiveJavaApplication {
public static void main(String[] args) {
SmartPhone samsungSmartPhone = SmartPhoneFactory.create(Type.SAMSUNG);
SmartPhone appleSmartPhone = SmartPhoneFactory.create(Type.APPLE);
System.out.println(samsungSmartPhone.toString()); // SamsungSmartPhone(name=갤럭시, version=6)
System.out.println(appleSmartPhone.toString()); // AppleSmartPhone(name=아이폰 미니, version=12)
}
}
그렇다면 단점은 뭘까?
- 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없습니다.
- 정적 팩토리 메서드는 프로그래머가 찾기 어렵습니다.
정적 팩토리 메서드 명명 방식
💡 from - 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
Date date = Date.from(instant);
💡 of - 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
💡valueOf - from, of보다 조금 더 자세하게 명명한 것일 뿐
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
💡instance, getInstance - (매개변수를 받는다면) 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지 않는다.
// StackWalker 클래스에 속해있는 객체들 중에서, 인수가 “apple”일 때 반환하는 객체를 반환할 것이다.
StackWalker luke = StackWalker.getInstance("apple");
💡create, newInstance - (매개변수를 받는다면) 매개변수로 명시된 인스턴스를 반환합니다. 이때 매번 새로운 인스턴스를 생성해 반환합니다.
Object newArray = Array.newInstance(classObject, arrayLen);
💡getType - 생성할 클래스가 아닌 다른 클래스에 팩토리 메서드를 정의할 때 사용합니다. Type은 팩토리 메서드가 반환할 객체의 타입 이름을 통틀어서 일컫는 말입니다.
FileStore fs = Files.getFileStore(path);
💡newType = 생성할 클래스가 아닌 다른 클래스에 팩토리 메서드를 정의할 때 사용합니다. 이때 매번 새로운 인스턴스를 생성해 반환함을 보장합니다.
BufferedReader br = Files.newBufferedReader(path);
💡결론
- 되도록이면 생성자보다는 정적 팩토리 메서드를 사용하자.
참고자료)
이펙티브 자바
728x90
반응형
'스터디 > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바 - Item7. 다 쓴 객체 참조를 해제하라. (0) | 2022.07.03 |
---|---|
이펙티브 자바 - Item6. 불필요한 객체 생성을 피하라. (0) | 2022.07.02 |
이펙티브 자바 - Item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라. (0) | 2022.07.02 |
이펙티브 자바 - Item4. 인스턴스화를 막으려거든 private 생성자를 사용하라. (0) | 2022.07.02 |
이펙티브 자바 - Item2. 생성자에 매개변수가 많다면 빌더를 고려하라. (0) | 2022.07.02 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- redis 대기열 구현
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- spring boot redis 대기열 구현
- spring boot 엑셀 다운로드
- pipe and filter architecture
- spring boot excel download oom
- spring boot poi excel download
- spring boot excel download paging
- transactional outbox pattern spring boot
- spring boot redisson destributed lock
- polling publisher spring boot
- 자바 백엔드 개발자 추천 도서
- @ControllerAdvice
- service based architecture
- 람다 표현식
- java userThread와 DaemonThread
- spring boot redisson 분산락 구현
- 레이어드 아키텍처란
- 공간 기반 아키텍처
- pipeline architecture
- redis sorted set으로 대기열 구현
- 트랜잭셔널 아웃박스 패턴 스프링부트
- transactional outbox pattern
- microkernel architecture
- JDK Dynamic Proxy와 CGLIB의 차이
- java ThreadLocal
- redis sorted set
- space based architecture
- spring boot redisson sorted set
- 서비스 기반 아키텍처
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함