디자인 패턴 Singleton Pattern 

 

싱글턴 패턴은 클래스 인스턴스가 하나만 만들어지도록 하고, 그 인스턴스에 대한 전역 접근을 제공합니다.

 

인스턴스 생성을 위해서 getInstance()를 호출해야 함.

 

싱글턴 패턴은 실제로 어떻게 적용할까요?

클래스에서 단 하나만 생성할 수 있게 관리합니다.

다른 클래스에서도 자신의 인스턴스를 추가로 만들지 못하도록 해야 합니다.

인스턴스를 생성하길 원하면 반드시 Singleton 클래스의 getInstance()를 호출해야 합니다.

 

인스턴스 접근을 어디에서도 가능하게 만들어야 합니다.

다른 객체에서 인스턴스가 필요할 때 언제든지 Singleton 클래스에 요청을 할 수 있게 만들고, 요청이 들어오면 getInstance()에서 만들어진 유일한 인스턴스를 반환해줘야 합니다.

 

Singleton Pattern 인스턴스를 이해하려면?

Singleton 인스턴스 사용

Singleton 인스턴스 사용을 유료 서비스 구독이라고 가정해봅니다.

유료 서비스를 이용할 때 구독이 안되어 있다면 최초 이용 시 구독 신청을 하게 됩니다.

구독 신청이 된 후 서비스를 제공받을 수 있습니다.

그리고 다시 서비스를 이용할 경우, 구독이 되어있다면 구독 신청 없이 바로 서비스를 제공받게 됩니다.

이미 구독 중이라면 다시 구독 신청을 할 필요는 없습니다.

 

 

Instance로 바꿔서 생각해보면?

위의 내용들을 Singleton Pattern의 인스턴스로 바꿔서 생각해보면 Singleton은 쉽게 느껴질 것입니다.

유료 서비스: Instance 호출

구독 중: Instance 존재 여부

구독 신청: Instance 생성

서비스 제공: Instance 제공

 

 

Singleton Class

package cg.park.designpattern.singleton;

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                    return singleton;
                }
            }
        }
        return singleton;
    }

}

 

Singleton 인스턴스를 호출 시 getInstance()를 호출해야 합니다.

객체가 Null 인지(생성된 적이 없는지) 확인을 합니다.

그리고 동시에 이 영역에 접근하지 못하게 synchronized를 선언합니다.

그리고 이 찰나에 null이 되었는지 체크를 다시 한 후에

Singleton 인스턴스를 생성 후 반환해줍니다.

Singleton 인스턴스가 이미 생성되었다면 바로 반환해줍니다.

 

synchronized를 제일 앞에 선언하지 않은 이유

synchronized가 있다면 이미 생성된 Singleton 인스턴스라도 모든 접근이 동기화되기 때문입니다.

동기화가 된다는 것은 인스턴스를 사용하기 위해 접근하려면 줄을 서야 하는 것입니다.

점심시간에 인기가 많은 식당을 가게 되면 줄을 서는 것처럼 Singleton 인스턴스를 사용하기 위해서는 줄을 서야 합니다.

하지만 이미 인스턴스가 있고 반환만 해주면 되는 상태라면 굳이 줄을 설 필요가 없습니다.

그래서 제일 위해 null 체크를 한 후, 인스턴스가 없는 경우에만 synchronized를 선언합니다.

synchronized가 선언되었기 때문에 1개의 인스턴스만 만들어지고, 그 후에는 이미 생성된 인스턴스를 반환할 것입니다.

 

구현

Paid라는 클래스를 만들어서 Singleton 인스턴스를 호출합니다.

Paid

package cg.park.designpattern.singleton;

public class Paid {

    public void perpare(String name) {
        System.out.println("유료 서비스 ========== START ==========");
        System.out.println(name +" 접속");
        Singleton.getInstance();
        System.out.println("유료 서비스 ========== E N D ==========");
    }
}

 

Singleton에 접근하면 로그를 남기도록 합니다.

Singleton

package cg.park.designpattern.singleton;

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    System.out.println("결제 후 서비스 제공");
                    singleton = new Singleton();
                    return singleton;
                }
            }
        }
        System.out.println("서비스 제공");
        return singleton;
    }

}

 

Paid를 호출할 Main 클래스를 만듭니다.

Main

package cg.park.designpattern.singleton;

public class Main {

    public static void main(String[] args) {
        Paid paid = new Paid();
        paid.perpare("1년 전");
        paid.perpare("반년 전");
        paid.perpare("현재");
    }

}

 

위의 로그를 보면 첫 번째 접근 시에만 결제 후 서비스를 제공하고 그다음부터는 바로 서비스를 제공합니다.

 

후기

Singleton Pattern은 꼭 필요한 경우가 아니라면 사용하지 않는 것을 추천합니다.

 

Git: https://github.com/qkrcksrbs8/designpattern

참조 자료: Head First Design Patterns

+ Recent posts