Abstract Class 를 어떻게 Singleton 으로 만들 수 있을까?

발생일: 2009.12.15

문제:
친구 홍이 문제점에 봉착했다며 묻는다.

Abstract Class 를 어떻게 Singleton 으로 만들 수 있을까?


Abstract Class 를 상속한 클래스들은 다 싱글턴으로 구현되도록 해야 한다고 한다.

음...
그럼 Abstrac class 안에 싱글턴 패턴인 getInstance  를 구현하되,
그 안에 팩토리 메서드 패턴으로 동적 타입으로 만들 수 있게 하면 안될까...?

private static AbstractSingleton uniqueInstance;
public static AbstractSingleton getInstance(Class cls) {
    // uniqueInstance 가 존재하는지 확인
    // 없을 경우, 동기화해서 class 에 따라 객체 생성해서 리턴
    // 있을 경우, uniqueInstance 리턴
}

이렇게 말이야...

음.... 가만,..
근데 이렇게 하면 uniqueInstance 에 단 하나의 인스턴스가 들어가버린다.
(즉, 한 번에 다형성을 무시하고 한 객체 밖에 생성할 수가 없다.)
게다가 Abstract Class 를 상속받는 클래스의 생성자가 private 일 테니 컴파일 에러도 나겠군...-_-;;
이렇게 하면 안되겠다.

흠.... 어떻게 해야할까...


해결책:
추상 클래스를 상속받은 아이를 위와 같은 방법으로 싱글턴으로 구현하는 건 불가능하다.

다만, 편법이 있으니, 아래와 같이 스태틱으로 인스턴스의 레지스트리를 만든 후에,
getInstance 에서 인스턴스에 저장된 값을 돌려주는 방법이 있다.

public abstract class AbstractSingleton {

    private static final Map<Class<? extends AbstractSingleton>, AbstractSingleton> instances
            = new HashMap<Class<? extends AbstractSingleton>, AbstractSingleton>();

    public static AbstractSingleton getInstance(Class<? extends AbstractSingleton> cls)
            throws InstantiationException, IllegalAccessException {
        if (instances.get(cls) == null) {
            synchronized (instances) {
                if (instances.get(cls) == null) {
                    instances.put(cls, cls.newInstance());
                }
            }
        }
        return instances.get(cls);
    }

    public abstract void setName(String name);

    public abstract String getName();
}


이 클래스를 상속받아 구현하는 서브 클래스는 아래와 같은 모습이다.

public class ConcreteSingleton extends AbstractSingleton {
    public String name;
   
    ConcreteSingleton() {} // private으로 구현하면 안된다. 기본 생성자로 만든다.
   
    public void setName(String name) {
        this.name = name;
    }
   
    public String getName() {
        return name;
    }
}


객체 생성은 아래와 같이 사용한다.

public class AbstractSingletonTest {
    public static void main(String[] args)
            throws InstantiationException, IllegalAccessException {
        // 객체를 생성한다.
        AbstractSingleton ac = AbstractSingleton.getInstance(ConcreteSingleton.class);
       
        System.out.println(ac.getName()); // null
        ac.setName("instance one"); // 객체의 이름을 설정한다
        System.out.println(ac.getName()); // instance one 출력
       
        // 두번 째 객체를 생성한다. 저장된 객체를 리턴한다.
        AbstractSingleton acOther = AbstractSingleton.getInstance(ConcreteSingleton.class);
        System.out.println(acOther.getName()); // instance one 출력
    }
}


안타깝게도, AbstractSingleton 클래스를 상속받는 서브 클래스의 생성자를 private 으로 설정할 수 없다.
다만 AbstractSingleton 클래스와 그 서브 클래스를 같은 패키지에 두고,
default 접근자로 작성하여 다른 패키지에서 해당 객체를 생성할 수 없도록 제한하는 정도에 만족해야겠다.



더 자세한 내용은 아래 페이지를 참고하자.

카테고리

분류 전체보기 (710)
About me. (6)
Daylogs (675)
영어공부 (0)
My works - 추억 (29)
비공개 (0)