이미 읽은지는 오래되었지만 Singleton pattern을 응용할 만한 기능이 있어 구현해 보았는데 실제로 유용하였다. 해당 소스는 실제 업무에서 사용하는 중이서 소스 코드에 대한 전체 설명은 하지 못하겠지만 개념적인 것을 한 번 살펴보고자 한다.
예를 들어, 카테고리와 같이 시스템 내에서 업데이트가 자주 발생하지 않는 테이블의 레코드일 경우 해당 테이블의 내용을 자주 디스플레이해야 하는 경우를 생각해 보자. 이럴 때 일반적으로는 해당 트랜잭션이 발생할 때마다 데이터베이스에 쿼리를 날린 후 리스트를 처리하게 된다. 이러한 일을 자주 업데이트되지 않는 정보를 매번 데이터베이스에 접속한 후 처리해서 가져오는 것은 전체적인 시스템의 성능상 낭비에 해당하는다. 이러한 트랜잭션이 많이 발생하지 않는 경우에는 별다른 문제를 발생시키지 않지만 그렇치 않은 경우에는 제법 많은 영향을 성능상에 문제를 발생시킨다.
따라서 이러한 경우에 싱클턴 패턴을 다음과 같이 사용해 보자. 해당 코드는 간단하게 실제로 사용해 본 소스를 여기에 맞게 재구성한 것이다. sudo code 형태로 재구성하였기 때문에 테스트를 직접해 보기에는 어려움이 있을수도 있을 것이다. sudo code 형태이긴 하지만 컴파일시에 에러를 발생시키지 않는다. 알고리즘에 해당하는 부분을 주석으로 설명하고 있기 때문인데 각자 업무에서 적용할만한 부분을 찾으면 된다.
/**
*
*/
package net.wiseant.designpattern.singleton;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author Sang Hyup Lee
* @version 1.0
*
*/
public class InformationSingleton {
private volatile static InformationSingleton singletonInstance;
private List informationList;
public InformationSingleton() {
// initinalize
initinalize();
}
public static InformationSingleton getInstance() {
if ( singletonInstance == null ) {
synchronized ( InformationSingleton.class ) {
if ( singletonInstance == null ) {
singletonInstance = new InformationSingleton();
}
}
}
return singletonInstance;
}
/**
* initinalize() 메소드에서는 InformationSingleton 인스턴스가 처음으로 생성될 때
* 수행할 초기화를 구동시킨다.
* 여기서는 해당 데이터베이스의 테이블 레코드를 리스트 형태로 얻어와서 List 객체에 대입해 둔다.
*/
public void initinalize() {
// TODO Auto-generated method stub
// 1. 데이터베이스 커넥션
// 2. informationList에 대입할 레코드의 리스트를 얻어온다.
}
/**
* update() 메소드에서는 해당 테이블에 추가 또는 업데이트 트랜잭션이 발생시
* 해당 추가/업데이트를 수행한 코드에서 호출하여 List 객체를 갱신해 준다.
*/
public void update() {
// 해당 레코드의 추가 또는 업데이트가 발생시 작동한다.
// informationList에 대입할 레코드의 리스트를 갱신한다.
}
/**
* @return the informationList
*/
public List getInformationList() {
return informationList;
}
public List getInformationListByType(int type) {
ArrayList resultList = new ArrayList();
Iterator informationIter = this.informationList.iterator();
while ( informationIter.hasNext() ) {
informationIter.next();
// 해당 Information 중에 type이 동일한 객체만 resultList에 add 한다.
}
return resultList;
}
}
위의 코드 중에서 설명이 필요한 부분은 대부분 주석에서 하고 있다. 다만 한가지 짚고 넘어갈 코드가 있다. 싱글턴 패턴을 구현하기 위해서 가장 중요한 부분으로 getInstance() 메소드 부분이다.
싱글턴 패턴으로 구현된 클래스를 클라이언트에서 호출하기 위해서는 객체네임.getInstance() 형태로 호출한게 되는데, 여기서는 InformationSingleton.getInstance() 가 된다.
이 메소드의 내용을 살펴보면 별다른 것은 없지만 생성된 인스턴스가 없을 경우에는 동기화 처리를 통하여 인스턴스를 생성한다.
이렇게 심플하게 생성되는 인스턴스는 별다른 문제를 발생시키지 않는다. 하지만 멀티스레딩이나 갑작스런 인스턴스의 잘못된 생성이 발생할 경우 인스턴스 생성에 문제가 발생하게 되는데, 이를 Java 1.4 이후 버전에서는 DCL(Dobule-Checking Locking)을 지원한다. 이렇게 지원하는 코드는 인스턴스명을 private으로 선언하는 부분에 있다.
private volatile static InformationSingleton singletonInstance;
volatile 키워를 Java 1.4 이후의 버전에서만 정상적으로 작동함으로 주의할 부분이라고 하겠다. 그러면 최종 코드는 완성된 것이다.
이와 같이 싱글턴 패턴을 사용할 경우 얻을 수 있는 장점은 해당 Information List를 얻어오는 부분이 하나의 인스턴스에서 처리됨으로 데이터베이스에 각 트랜잭션마다 쿼리로 처리하지 않고도 해당 리스트를 원하는 형태로 얻어갈 수 있다. 원하는 리스트의 형태는 각 메소드를 언제든지 생성하여 제공할 수 있다.
이로서 불필요한 데이터베이스로의 접근을 막음으로 서버 사이드 시스템내에서는 성능을 높일 수 있는 방안이라고 할 수 있다. 간혹 DBMS에서는 이러한 일을 메모리 DB 형태로 제공하기도 한다고 한다. 이 내용은 필자도 연구해 보아야 할 필요가 있을 듯 하다.
최종적으로 디자인 패턴의 가장 큰 장점은 이미 많은 경험을 통한 하나의 패턴 속에 넣어둠으로서 문제를 작게 발생시키거나 보다 효율적이 코드를 구현하기 위함에 있다. 싱글턴 패턴도 간단한 개념이고 구현도 간단하지만 적절하게 적용할 부분을 애플리케이션에서 찾는다면 위와 같은 문제들을 잘 해결할 수 있으리라 본다.