일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 컴퓨터공학
- 정보처리기사
- 스프링
- 알고리즘
- CS
- 신입
- 깃
- 뮤텍스
- 공채
- 프로그래밍
- Effective Java
- 세마포어
- OS
- Public
- 컴퓨터과학
- 디지털
- 운영체제
- 깃허브
- github
- 개발
- 자바
- 신입사원
- IT
- java
- 우리카드
- 메모리
- 이펙티브 자바
- 스터디
- package-private
- spring
Archives
- Today
- Total
주니어 개발자 성장기
아이템 3. private 생성자나 열거 타입으로 싱글톤임을 보증하라. 본문
싱글톤을 사용하는 경우
크게 2가지 경우에 싱글톤을 사용하면 된다.
- 싱글톤을 사용하면 좋은 경우
- DB 커넥션 등 인스턴스 생성 비용이 비싼 경우
- 반드시 사용해야 하는 경우
- 환경설정 등 애플리케이션 전반에 하나만 존재해야 하는 경우
Private 생성자
이펙티브 자바에서는 private 생성자를 기본으로 싱글톤을 구현하는 방법을 소개하고 있다.
그 중 첫 번째 방법인 public static final 멤버 변수를 사용하는 방법은 다음과 같다.
public static final Elvis INSTANCE = new Elvis();
즉, 필요 할 때마다 Elvis. INSTANCE
를 직접 호출하면 된다.
문제점
- 테스트하기 어려운 구조
싱글톤 클래스를 사용하는 클라이언트 코드의 테스트가 어려워진다. 인터페이스라면 Mock 객체를 만들 수 있지만, 인터페이스 기반이 아니라면 Mocking이 어렵기 때문이다.
- 리플렉션을 통해서 private 생성자를 호출할 수 있다. 즉, 리플렉션으로 새로운 인스턴스가 생성되어 싱글톤이 훼손(공격)될 수 있다.
생성자에 인스턴스 생성후에 생성자가 호출되면 예외를 던지는 코드를 추가할 수 있다.
public static final Elvis INSTANCE = new Elvis();
private static boolean created;
private Elvis() {
if (created) {
throw new UnsupportedOperationException("can't be created by constructor.");
}
created = true;
}
(하지만 간결함이라는 장점을 잃는다.)
- 역직렬화 시 새로운 인스턴스가 생성된다. (의도와 다르게 동작한다)
직렬화(Serialize)란?
직렬화란 객체 정보를 어딘가에 저장하는 것, 역직렬화는 어딘가에 저장되어 있는 직렬화된 객체 정보를 읽어 오는 것이다.
Spring을 사용하거나 readResolve() 메서드를 구현하여 해결 가능하다.
readResolve()는 Overriding이 아니지만 Overriding처럼 사용하면 된다.
private Object readResolve() {
return INSTANCE;
}
정적 팩터리 메서드
private 생성자를 사용하는 두 번째 방법으로 위의 public static final 멤버 변수를 사용하는 방법과 같은 단점을 가지고 있다.
private static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public static Elvis getInstance() { return INSTANCE; }
장점
- 정적 메서드를 통해 인스턴스를 접근하게 하면, 싱글톤을 사용할지 매번 다른 인스턴스를 생성할 지 클라이언트 코드의 변경없이 변경할 수 있다.
- 원한다면 제네릭 싱글톤 팩터리를 사용할 수 있다.
public class MetaElvis<T> {
private static final MetaElvis<Object> INSTANCE = new MetaElvis<>();
private MetaElvis() { }
@SuppressWarnings("unchecked")
public static <E> MetaElvis<E> getInstance() { return (MetaElvis<E>) INSTANCE; }
public void say(T t) {
System.out.println(t);
}
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
}
이렇게 하면 같은 싱글톤 인스턴스를 사용하지만 제네릭 타입에 따라 용도를 다르게 사용할 수 있다. equals()
비교시 true 를 반환한다.
public static void main(String[] args) {
MetaElvis<String> elvis1 = MetaElvis.getInstance();
MetaElvis<Integer> elvis2 = MetaElvis.getInstance();
System.out.println(elvis1);
System.out.println(elvis2);
elvis1.say("hello");
elvis2.say(100);
}
// 출력결과
me.whiteship.chapter01.item03.staticfactory.MetaElvis@7f690630
me.whiteship.chapter01.item03.staticfactory.MetaElvis@7f690630
hello
100
- 정적 팩터리의 메서드 참조를 공급자(Supplier)로 사용할 수 있다.
Elvis::getInstance
'Java > 이펙티브 자바' 카테고리의 다른 글
아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라. (0) | 2023.07.15 |
---|---|
아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2023.07.13 |
아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라. (0) | 2023.07.11 |
아이템 1. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2023.06.24 |
개요 (0) | 2023.06.24 |