본문 바로가기
Web

[Spring] Singleton Pattern

by DuncanKim 2022. 7. 22.
728x90

[Spring] Singleton Pattern

객체지향 디자인패턴 중 싱글턴 패턴은 객체의 인스턴스를 오직 1개만 생성한다. 이것이 싱글턴 패턴의 정의이다.

커넥션 풀, 스레드 풀, 디바이스 설정 객체 등과 같은 것을 인스턴스를 여러 개 만들어 사용하게 되면 불필요한 메모리 자원 낭비가 되는데, 이럴 때 사용하는 것이다.

 

이런 싱글턴 패턴을 구현하기 위해서는 반드시 객체 생성을 위한 new에 제약을 걸어야 한다. 또한 만들어진 단일 객체를 반환할 수 있는 메서드가 필요하다. 그래서 아래의 세 가지 조건이 필요하다.

 

- new를 외부에서 실행할 수 없도록 생성자에 private 접근 제어자를 지정한다.
- 유일한 단일 객체를 반환할 수 있는 정적 메서드가 필요하다.
- 유일한 단일 객체를 참조할 정적 참조 변수가 필요하다.

 

1) 싱글턴 패턴 예시 1

 

사람생성() 이라는 메서드를 통해서 하나의 인스턴스를 생성하고 이를 활용해서 나이를 설정하는 것이다.

만약, b사람이 생긴다고 하였을 때, 똑같이 .사람생성()을 호출한다면, a사람에 붙어있는 객체를 b사람도 참조하게 될 것이다.

//code.oa.gg/java8/1073
// 문제 : 사람객체를 생성한 후 나이를 22살로 만들어주세요.
// 조건 : 사람 생성자는 수정/추가할 수 없습니다.
// 조건 : 아래와 같이 출력 되어야 합니다.
class Main {
	public static void main(String[] args) {
		사람 a사람;
		
		a사람 = 사람.사람생성();
		a사람.set나이(22);
		
		System.out.println("사람의 나이는 " + a사람.get나이() + "살 입니다.");
		// 출력 : 사람의 나이는 22살 입니다.
	}
}

class 사람 {
	private int 나이;
	
	private 사람() {
	}
	
	// 힌트 : 여기에 메서드 3개 추가해야 합니다.
	public static 사람 사람생성(){
		return new 사람();
	}
	void set나이(int 나이){
		this.나이 = 나이;
	}
	
	int get나이(){
		return 나이;
	}
}

 

2) 싱글턴 패턴 예시 2

 

이 또한 위의 코드와 마찬가지로, 하나의 객체를 여러 배열에서 참조하고 있는 형식이다.

실제로 사람들[0]부터 사람들[4]에 들어있는 객체의 주소를 출력해보면 모두 동일한 것을 알 수 있다.

//code.oa.gg/java8/1075
// 문제 : 아래코드가 실행되도록 해주세요.

class Main {
	public static void main(String[] args) {
		사람[] 사람들 = new 사람[5];
		사람들[0] = 사람.get사람();
		사람들[1] = 사람.get사람();
		사람들[2] = 사람.get사람();
		사람들[3] = 사람.get사람();
		사람들[4] = 사람.get사람();

		System.out.println("실행완료");
	}
}

class 사람{
    private 사람(){
    
    }
    
    static 사람 get사람(){
        return new 사람();
    }
}

 

3) 싱글턴 패턴 예시 3

 

똑같은 객체를 참조하는데, 여기에서는 '번호'를 통해서 서로 다른 숫자를 가질 수 있도록 하고 있다.

어떤 숫자를 외부에서 집어넣어 바꾸는 것이 아니라, 클래스 내부적으로 호출이 몇 번 되느냐에 따라서 '번호'를 올리는 것이다.

숫자는 다르게 프린트 되지만, 결국은 모두 같은 객체를 참조하고 있음을 알아야 한다.

 

//code.oa.gg/java8/1078
// 문제 : 아래코드가 실행되도록 해주세요.[정답]

class Main {
	public static void main(String[] args) {
		사람[] 사람들 = new 사람[5];
		사람들[0] = 사람.get사람();
		사람들[1] = 사람.get사람();
		사람들[2] = 사람.get사람();
		사람들[3] = 사람.get사람();
		사람들[4] = 사람.get사람();
		
		for ( int i = 0; i < 사람들.length; i++ ) {
			사람들[i].자기소개();
		}
		
		/*
		// 출력
		저는 1번째 사람입니다.
		저는 2번째 사람입니다.
		저는 3번째 사람입니다.
		저는 4번째 사람입니다.
		저는 5번째 사람입니다.
		*/
	}
}


class 사람 {
	static private int 사람수;
	private int 번호;
	
	// static 요소 전용 생성자
	// 따로 뭔가를 호출하지 않아도 프로그램이 실행되면 가장먼저 실행된다.
	static {
		사람수 = 0;
	}
	
	private 사람(int 번호) {
		this.번호 = 번호;
	}
	
	static 사람 get사람() {
		사람 a사람 = new 사람(사람수 + 1);
		사람수++;
		return a사람;
	}
	
	public void 자기소개() {
		System.out.println("저는 " + 번호 + "번째 사람입니다.");
	}
}

 

4) 싱글턴 패턴 예시 4

 

여기도 마찬가지로 똑같은 객체를 참조한다. static 필드를 통해서 일정 조건이 되면 더 이상 숫자가 올라가지 않게 제한을 둔 것이 전부이다.  숫자는 다르게 프린트 되지만, 결국은 모두 같은 객체를 참조하고 있음을 알아야 한다.

 

//code.oa.gg/java8/1079
// 문제 : 아래코드가 실행되도록 해주세요.
// 조건 : 사람 객체의 수는 3을 넘을 수 없습니다.

class Main {
    public static void main(String[] args) {
        사람[] 사람들 = new 사람[7];
        사람들[0] = 사람.get사람();
        사람들[1] = 사람.get사람();
        사람들[2] = 사람.get사람();
        사람들[3] = 사람.get사람();
        사람들[4] = 사람.get사람();
        사람들[5] = 사람.get사람();
        사람들[6] = 사람.get사람();
        
        for ( int i = 0; i < 사람들.length; i++ ) {
            사람들[i].자기소개();
        }
        
        /*
        // 출력
        저는 1번째 사람입니다.
        저는 2번째 사람입니다.
        저는 3번째 사람입니다.
        저는 3번째 사람입니다.
        저는 3번째 사람입니다.
        저는 3번째 사람입니다.
        저는 3번째 사람입니다.
        */
    }
}

class 사람 {
    private int id;
    private static int lastId;
    private static 사람 가장_마지막에_생성된_사람;
    
    static {
        lastId = 0;
    }
    
    public static 사람 get사람() {
        if ( lastId == 3 ) {
            return 가장_마지막에_생성된_사람;
        }
        
        int id = ++lastId;
        사람 a사람 = new 사람(id);
        가장_마지막에_생성된_사람 = a사람;
        
        return a사람;
    }
    
    public 사람(int id) {
        this.id = id;
    }
    
    public void 자기소개() {
        System.out.printf("저는 %d번째 사람입니다.\n", id);
    }
}

 

 

5) 싱글턴 패턴의 단점

 

싱글턴 패턴은 객체 지향 설계 원칙(SOLID) 중 개방-폐쇄 원칙에 위배될 수 있는 한계점을 가지고 있다.

싱글턴 인스턴스가 혼자 많은 일을 하거나, 많은 데이터를 공유시키면, 다른 클래스와 결합도가 높아지게 되는데, 이때, 개방-폐쇄 원칙이 위배된다. 결합도가 높아지면 유지보수가 힘들고, 결과적으로 테스트도 원활하게 진행할 수 없는 문제점이 발생한다.

 

또한 멀티 스레드 환경에서 동기화 처리를 하지 않았을 때, 인스턴스가 2개 생성되는 문제도 발생할 수 있다.

 

728x90

'Web' 카테고리의 다른 글

[Spring] Observer Pattern  (0) 2022.07.24
[Spring] Factory-Method Pattern  (0) 2022.07.23
[Java] equals()와 ==의 차이점  (0) 2022.07.19
[Java] Wrapper 클래스  (0) 2022.07.18
[Java] throws  (0) 2022.07.17

댓글