티스토리 뷰
ㆍ싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안됩니다.
ㆍ무상태로 설계해야 한다.!
ㆍ특정 클라이언트에 의존적인 필드가 있으면 안됩니다.
ㆍ특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됩니다.
ㆍ가급적 조회만 할 수 있어야 합니다.
ㆍ필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 합니다.
문제가 발생하는 코드 예시
ㆍStatefulService Class는 싱글톤으로 선언된 클래스이며, getPrice 메서드를 사용하여 user들의 price를 반환
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
|
package com.example.singletom;
public class StatefulService {
// 싱글톤 선언 시작
private static final StatefulService instance = new StatefulService();
public static StatefulService getInstance() {
return instance;
}
private StatefulService() {}
// 싱글톤 선언 끝
private int price; //상태를 유지하는 필드
public void order(String name, int price) {
this.price = price; // 문제 발생
}
public int getPrice() {
return this.price;
}
}
|
cs |
ㆍuserA와 userB는 각각 1만원과 2만원을 주문하였지만 StatefulService Class에서 price 필드는 마지막 userB의 price로 값을 유지하기 때문에 아래와 같은 문제점이 발생합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
StatefulService statefulService1 = StatefulService.getInstance();
StatefulService statefulService2 = StatefulService.getInstance();
// A 사용자가 1만원 주문
statefulService1.order("userA", 10000);
// B 사용자가 2만원 주문
statefulService2.order("userB", 20000);
// 사용자A가 주문 금액 조회
// 사용자 B가 중간에 끼어들어 price는 2만원이 나옴
int price = statefulService1.getPrice();
System.out.println(price); // 20000만원 출력
}
}
|
cs |
문제가 해결된 코드 예시 (스프링을 쓰지않는 코드)
ㆍ기존 StatefulService Class에 선언되어 있던 공유 필드인 price 필드를 삭제합니다.
ㆍ또한 getPrice 메서드 대신 order 메서드를 반환값으로 price를 반환합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.example.singletom;
public class StatefulService {
// 싱글톤 선언 시작
private static final StatefulService instance = new StatefulService();
public static StatefulService getInstance() {
return instance;
}
private StatefulService() {}
// 싱글톤 선언 끝
//private int price; //상태를 유지하는 필드 삭제
public int order(String name, int price) {
System.out.println("name : " + name + " price : "+ price);
return price;
}
}
|
cs |
ㆍ order 매서드는 자신의 price를 반환하므로 userA와 userB는 각각 자신의 금액인 1만원과 2만원이 정상적으로 출력되는 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
StatefulService statefulService1 = StatefulService.getInstance();
StatefulService statefulService2 = StatefulService.getInstance();
// A 사용자가 1만원 주문
int userA = statefulService1.order("userA", 10000);
// B 사용자가 2만원 주문
int userB = statefulService2.order("userB", 20000);
// 사용자가 주문 금액 조회
System.out.println(userA); // 10000만원 출력
System.out.println(userB); // 20000만원 출력
}
}
|
cs |
문제가 해결된 코드 예시 (스프링 코드)
ㆍ스프링은 싱글톤을 알아서 해주므로 코드를 길게 적을 필요가 없어집니다.ㅎㅎ
1
2
3
4
5
6
7
8
|
public class StatefulService {
public int order(String name, int price) {
System.out.println("name : " + name + " price : "+ price);
return price;
}
}
|
cs |
정상적으로 출력되는것을 볼 수 있습니다.
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
|
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// A 사용자가 1만원 주문
int userA = statefulService1.order("userA", 10000);
// B 사용자가 2만원 주문
int userB = statefulService2.order("userB", 20000);
// 사용자 주문 금액 조회
System.out.println(userA); // 10000만원 출력
System.out.println(userB); // 20000만원 출력
}
static class TestConfig{
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
|
cs |
'JAVA > Design_Pattern' 카테고리의 다른 글
[Design_Pattern] 프로토 타입 패턴 (0) | 2022.02.28 |
---|---|
[Design_Pattern] 빌더 패턴 (0) | 2022.02.21 |
[Design_Pattern] 전략 패턴(Strategy Pattern) (0) | 2022.02.17 |
[Design_Pattern] 템플릿 메서드 패턴 (0) | 2022.02.16 |
[Design_Pattern] 템플릿 콜백 패턴(Template Callback Pattern) (0) | 2021.06.30 |
- Total
- Today
- Yesterday
- redis sorted set
- transactional outbox pattern spring boot
- spring boot redisson destributed lock
- redis sorted set으로 대기열 구현
- spring boot 엑셀 다운로드
- transactional outbox pattern
- JDK Dynamic Proxy와 CGLIB의 차이
- spring boot poi excel download
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- @ControllerAdvice
- pipeline architecture
- 자바 백엔드 개발자 추천 도서
- space based architecture
- 공간 기반 아키텍처
- spring boot redisson sorted set
- spring boot excel download oom
- spring boot excel download paging
- microkernel architecture
- 람다 표현식
- service based architecture
- java ThreadLocal
- 서비스 기반 아키텍처
- 레이어드 아키텍처란
- redis 대기열 구현
- spring boot redisson 분산락 구현
- java userThread와 DaemonThread
- spring boot redis 대기열 구현
- pipe and filter architecture
- polling publisher spring boot
- 트랜잭셔널 아웃박스 패턴 스프링부트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |