일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 쓰레드
- 운영체제
- 그리디 알고리즘
- 멀티쓰레드
- Direct12
- 프로그래머스
- 동적계획법
- 그리디알고리즘
- 디자인패턴
- directx
- I/O장치
- 다이나믹 프로그래밍
- 스케줄링
- 백준
- 병행성 관련 오류
- 알고리즘
- DirectX12
- 영속성
- 다이나믹프로그래밍
- 락
- 컨디션 변수
- DirectX 12
- 멀티프로세서
- 자료구조
- 타입 객체
- OS
- 렌더링 파이프라인
- 병행성
- codility
- 파일시스템 구현
- Today
- Total
기록공간
[Effective Java] Item03. private 생성자나 열거 타입으로 싱글턴임을 보증하라 본문
싱글턴이란?
싱글턴이란 디자인 패턴에서 생성 패턴 중 하나로 오직 하나의 인스턴스만 생성하도록 약속하는 패턴이다.
보통 싱글턴 패턴은 무상태(Stateless) 객체나 시스템 객체같 유일해야 하는 객체를 생성하는데에 사용된다.
싱글턴 패턴의 한계는 그 특성에서도 알 수 있다. 하나의 인스턴스만 생성 가능 하다는 특성 말이다.
싱글턴을 사용하려는 클라이언트를 테스트하기가 어려워 질 수 있다. 왜냐하면 테스트를 위한 가짜(mock)객체를 구현하는 것으로는 싱글턴을 대체 할 수 없기 때문이다.
싱글턴 생성 방법
첫번째 방법은 public 정적 final 멤버변수를 사용하는 방법이다.
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
INSTANCE 멤버변수를 생성하여 Elvis 인스턴스를 선언과 동시에 초기화하고 있다.
생성자를 private으로 두었기 때문에 내부 클래스에서만 접근이 가능하다는 것도 주목해야한다.
이렇게 하면 java는 클래스를 생성과 동시에 초기화 하면서 Elvis의 인스턴스가 딱 한 번 생성된다.
때문에 Elvis는 전체 시스템에서 인스턴스가 하나임을 보장한다.
이 방법의 장점은 해당 클래스가 싱글턴임을 명백히 할 수 있다는 것이다.
public static 필드가 final이니 다른 객체가 참조 할 수 없기 때문이다.
또 다른 장점은 간결하다는 것이다.
하지만, 이 방법은 리플렉션 API인 AccesibleObject.setAcceisible() 을 사용하여 private 생성자를 호출할 수 있다.
이러한 공격은 예외 처리를 통해 막을 수 있다.
두번째 방법은 정적 팩터리 메서드를 사용하는 방법이다.
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { ... }
}
정적 팩터리 메서드를 통해서만 해당 변수를 반환하도록 변경되었다.
멤버변수는 private이므로 INSTANCE에 직접 접근할 수 없다.
이 방법의 장점은 API를 바꾸지 않아도 싱글턴이 아니게 변경 할 수 있다.
유일한 INSTANCE를 반환하던 팩터리 메서드를 호출 스레드별로 다른 인스턴스로 넘겨줄 수 있다.
또 다른 장점은 정적 팩터리 메서드를 공급자로 사용 할 수 있다는 것이다.
Elvis::getInstance에서 Supplier<Elvis> 형식으로 변경하면 된다.
하지만 이러한 장점들이 필요 없는 경우에는 첫번째 방법을 사용하는 것이 더 바람직하다.
세번째 방법은 열거타입 방식의 싱글턴을 사용하는 것이다.
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
위의 두 방법보다 훨씬 더 간결하고 추가 노력을 하지 않아도 직렬화가 가능하다.
또한 복잡한 상황에서도 제 2의 인스턴스를 생성하는 상황을 막아준다.
단점은 enum 외의 클래스를 상속받은 경우에 사용이 불가능 하다는 것이다.
'Java > Effective Java' 카테고리의 다른 글
[Effective Java] Item02. 생성자의 매개변수가 많다면 빌더를 고려하라 (0) | 2021.12.21 |
---|---|
[Effective Java] Item01. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2021.12.17 |