달력

02

« 2012/02 »

  •  
  •  
  •  
  • 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
  •  
  •  
  •  
다음은 Singleton pattern 예제 코드이다.

/**
 * Singleton.java
 * 
 * Singleton pattern의 예제
 */
package net.wiseant.designpattern.singleton;

/**
 * @author Sang Hyup Lee
 * @version 1.0
 *
 */
public class Singleton {

    private volatile static Singleton uniqueInstance;
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        if ( uniqueInstance == null ) {
            synchronized (Singleton.class) {
                if ( uniqueInstance == null ) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        
        return uniqueInstance;
    }
    
}
Posted by korcslewis
2009/05/12 17:46

Singleton pattern의 응용 샘플 Design patterns2009/05/12 17:46

얼마전까지 Head First Design Pattern 책을 통해서 디자인 패턴에 대한 공부하는 중이었다. 지금도 언제나 공부는 해야 하지만 시간이 있을 때 보아두는 게 좋을 거란 생각이 들었다.

이미 읽은지는 오래되었지만 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 형태로 제공하기도 한다고 한다. 이 내용은 필자도 연구해 보아야 할 필요가 있을 듯 하다.

최종적으로 디자인 패턴의 가장 큰 장점은 이미 많은 경험을 통한 하나의 패턴 속에 넣어둠으로서 문제를 작게 발생시키거나 보다 효율적이 코드를 구현하기 위함에 있다. 싱글턴 패턴도 간단한 개념이고 구현도 간단하지만 적절하게 적용할 부분을 애플리케이션에서 찾는다면 위와 같은 문제들을 잘 해결할 수 있으리라 본다.
Posted by korcslewis
몇 일동안 Head First Design Pattern을 다시 보고 있는 중이다. 이전에 놓친 부분도 많은 거 같고, 항상 중요한 것을 잊어버릴 수 밖에 없기에 다시한 번 다짐을 해 보고자 읽기 시작했는데, 도움이 많이 된다.

이번에 소개할 패턴은 Adapter Pattern 인데 실제 설명과 내용은 책을 참조하면 된다. Head First Design Patten은 워낙 유명한만큼 좋은 책이니 한 번 씩 읽어보길 바란다.
이 책의 Adapter Pattern에서 제공하는 예제 중에 Enumeation Collection 객체를 Iterator Collection 객체 형태로의 adapter로서 작동할 수 있는 EnumerationIterator 클래스를 제공한다. 이 클래스의 소스 코드와 테스트 할 수 있는 코드를 여기서 제공한다.

실제 업무 상에 Enumeration 객체를 통해서 각각의 Object를 가져오는 경우가 많은데 이를 Iterator를 사용하는 형태로 EnumerationIterator 클래스를 사용하면 가져올 수 있다.

[EnumerationIterator.java]
/**
 * 
 */
package net.wiseant.designpattern.adaptor;

import java.util.Enumeration;
import java.util.Iterator;

/**
 * @author Sang Hyup Lee
 * @version 1.0
 *
 */
public class EnumerationIterator implements Iterator {

    Enumeration enumeration;
    
    public EnumerationIterator(Enumeration enumeration) {
        this.enumeration = enumeration;
    }
    
    /* (non-Javadoc)
     * @see java.util.Iterator#hasNext()
     */
    public boolean hasNext() {
        // TODO Auto-generated method stub
        return this.enumeration.hasMoreElements();
    }

    /* (non-Javadoc)
     * @see java.util.Iterator#next()
     */
    public Object next() {
        // TODO Auto-generated method stub
        return this.enumeration.nextElement();
    }

    /* (non-Javadoc)
     * @see java.util.Iterator#remove()
     */
    public void remove() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException();
    }

}


다음은 위의 코드를 테스트해 볼 수 있는 간단한 Test 코드이다.

/**
 * IteratorAdapterTest.java
 */
package net.wiseant.designpattern.adaptor;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

/**
 * @author Sang Hyup Lee
 * @version 1.0
 *
 */
public class IteratorAdapterTest {
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        Vector v = new Vector();
          
        v.addElement(new String("Java"));
        v.addElement(new String("Eclipse"));
        v.addElement(new String("Spring"));
          
        Enumeration enu = v.elements();
        
        /*
        Iterator iter = v.iterator();
        while ( iter.hasNext() ) {
            String temp = ""+iter.next();
            System.out.println(temp);
        }
        */
        
        EnumerationIterator iteratorAdapter = new EnumerationIterator(enu);
        
        while ( iteratorAdapter.hasNext() ) {
            String temp = ""+iteratorAdapter.next();
            System.out.println(temp);
        }
    }
    
}


테스트 코드의 주석부분은 Vector가 Iterator 형태로도 제공해 준다는 것을 명시하고 있다. 하지만 실제 코드의 에제는 Enumeration 형태로 엘리먼트 또는 각 객체를 EnumerationIterator Adapter 클래스를 이용하는 방법을 보여주고 있다.
Posted by korcslewis
코드를 모듈화한다는 것은 알고리짐의 기본원칙에 입각한 것이다. 즉, "나누어서 정복하라"라고 하는 알고리즘에서 기인한 것이라고 할 수 있는데, 현업에서 이를 쉽게 적용하기가 쉽지 않다.
이론상으로는 가능한한 작은 토막을 내어서 모듈화를 시키는 것이 좋다고 하지만 실제로는 업무를 진행하는 데 있어 이론이 정확하게 적용되는 것은 아닌 것이다.

그러함에도 개발자들은 자신의 코드를 통해서 이렇게 제시된 이론에 근접한 코드를 짤 수 있어야 한다고 생각한다. 쉽지 않은 길이지만 경력이 쌓여갈수록 이러한 이론에 접근하여 잘 짜여진 코드를 만들수 있어야 하는게 개발자의 길이기도 하다.

그러면 간략하게 모듈화에 대해 메모를 해보자. C 계열의 언어에서는 function이나 Implementation 코드로 구현이 가능하고, 자바언어에서는 method나 클래스로 구현이 가능하다. 이렇게 언어에 국한적인 원칙 외에 일반적인 원칙을 간단하게 메모해 둔다.


1. 코드의 공통된 부분을 찾아서 모듈화 시켜라
이것은 가장 일반적인 원칙 중에 하나인데 개발자들에겐 당연한 일인듯이 여기진다. 그러함에도 실제 코드를 살펴보면 상당부분 중복된 부분을 살펴볼 수 있는데, 첫째는 잘못된 설계에서 기인하고 때로는 지나친(잘못된) 요구사항 중에 실수를 유발하기도 한다. 둘째는 일종의 리팩토링을 하지 않았다고 볼 수 있다. 이미 짜여진 코드는 완벽할 수 가 없다. 그럼으로 때에 따라서는 코드 리팩토링을 통해서 공통된 부분을 찾아서 모듈화를 진행시켜야 한다.


2. 디자인 패턴의 적용(OCP : Open-Closed Principle 의 적용)
디자인 패턴의 정의를 내릴 수는 없지만 쉽게 이해할 수 있는 바로는 우리보다 휠씬 똑똑한 사람들이 개발을 진행하는데 있어 공통적으로 사용되거나 자주 사용되는 원칙을 정리해 둔 것이라 할 수 있다. 따라서 디자인 패턴의 적용은 대부분 리팩토링이나 모듈화를 진행하는데 있어 아주 중요한 역할을 한다.
여기서 디자인 패턴에 대한 부분을 언급하기에는 무리가 있음으로 OCP에 대한 자료를 소개하는 것으로 대체한다.

위의 연재에서 제시하는 대부분의 원칙들이 디자인패턴에서 제시하는 원칙과 일치하는 것을 알아둬야 한다. 끝으로, 여러가지의 디자인 패턴중에 프로젝트에 맞는 패턴을 적용할 줄 아는 능력을 키워야하겠다.



3. 잘 구성된(짜여진) 코드 분석
잘 짜여진 코드는 위의 두 원칙에 입각하여 제대로 된 코드를 제시하는 것을 볼 수 있다. 이전에 자바 진영에서는 SUN Microsystems에서 Blue-print로 제시된 petstore가 있었는데, 이 소스는 잘 짜여진 소스일 뿐 만 아니라, 이후에 다양한 프레임워크를 개발한 곳에서 재구성되고 또 재구성되어 각 프레임워크에 맞는 형태로 개발되어져 있다.
따라서 당연히 발전에 발전을 거듭하여 우리가 분석해 보고 따라해 보기에 좋을 것이다.


4. 개발업무의 적절한 분석
모듈화의 일반적인 원칙을 제시하면서 개발업무에 대한 적절한 분석을 제시하는 것이 이상하게 여겨질런지도 모르겠다. 하지만 업무에 대한 적절한 분석은 설계와 구현으로 직결되는 것이기 때문에 이미 분석된 예제코드와 같은 것이 아니고, 자신이 직접 설계하고 구현해야 하는 프로젝트에 맞는 업무일 경우 이에 대한 분석은 보다 확실히 이루어져야 한다.
물론 우리나라의 사정상 제대로된 요구사항이나 업무분석이 이루어지기 힘들다는 것을 알고 있다. 여기선 주변의 환경적인 요소를 이야기하는 것이 아니라, 개발자에게 있어 필요한 사항을 이야기하는 것임으로 집고 넘어가야 한다.

예들들면, 업무를 어떻게 나누느냐에 따라서 비지니스 로직이 변경되기 때문에 그에 따른 도메인이나 퍼시스턴스 영역들이 변경될 가능성이 높아진다. 사실은 도메인 설계시에 다양한 요구사항에 맞는 설계를 해야겠지만, 현실은 그렇치 않음으로 생각을 역으로 바꾸어 업무처리 중에 공통된 부분과 모듈화 할 수 있는 부분을 찾아서 처리해 주는 것이 좋다.


5. 유틸리티 코드 및 자료구조(또는 Collection) 코드의 적절한 이용
약간은 엉뚱하다. 하지만 오랫동안 개발해 온 사람들을보면 나름대로의 유틸리티 코드를 가지고 있는 것을 본다. 예를들면, Encoding, Decoding 코드라든지, ByteUtil 같은 코드, 그리고 자료구조의 구현과 같은 코드들이다.
자료구조는 알고리즘 이전에 반드시 필수적으로 봐야하는 과목(?)으로 개발을 하면 할 수록 알고리즘과 함께 중요하게 여기지는 파트이다. 자바에서는 이러한 자료구조와 같은 것들을 Collection 객체에서 많이 구현해 주고 있다. 그러함에도 자신에게 맞는 코드를 구비할 필요가 있다. 당연히 Collection 클래스들을 이용하는 것이 좋다.



글을 적음에 있어 약간의 즉흥성이 발동을 했다. 오늘 업무를 진행하다가 느닷없는 수정사항으로 어떻게 처리를 할까? 하면서 코드를 살펴보던 중에 중복된 업무에 대한 처리를 이루어지는 것을 보면서 나름대로의 모듈화를 진행하고 테스트 및 배포를 완료해 놓고 이 글을 작성하기 때문이다.
평소엔 전혀 생각지도 못했던 원칙들이 글을 작성하면서 내 스스로에게 전달하고픈 메시지가 되어 버렸기도 하다. 한편으론 사견이라 할 수 있지만 많은 사람들에게 조금이라도 도움이 되었으면 하는 바램이다.
Posted by korcslewis